diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7ad6379 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.PublicKeyModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel + +class AuthenticateViewModel : BaseViewModel() { + + private val gson = Gson() + val keyModel = MutableLiveData() + + fun obtainPublicKey() = launch({ + val response = RetrofitServiceManager.authenticate() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + keyModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7ad6379 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.PublicKeyModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel + +class AuthenticateViewModel : BaseViewModel() { + + private val gson = Gson() + val keyModel = MutableLiveData() + + fun obtainPublicKey() = launch({ + val response = RetrofitServiceManager.authenticate() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + keyModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt new file mode 100644 index 0000000..5211816 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt @@ -0,0 +1,55 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.model.LoginResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class LoginViewModel : BaseViewModel() { + + private val gson = Gson() + val enterResultModel = MutableLiveData() + val outResultModel = MutableLiveData() + + fun enter(sid: String, account: String, secretKey: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.login(sid, account, secretKey) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + "登录成功".show(BaseApplication.obtainInstance()) + enterResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) + + fun out() = launch({ + val response = RetrofitServiceManager.loginOut() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + outResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7ad6379 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.PublicKeyModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel + +class AuthenticateViewModel : BaseViewModel() { + + private val gson = Gson() + val keyModel = MutableLiveData() + + fun obtainPublicKey() = launch({ + val response = RetrofitServiceManager.authenticate() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + keyModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt new file mode 100644 index 0000000..5211816 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt @@ -0,0 +1,55 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.model.LoginResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class LoginViewModel : BaseViewModel() { + + private val gson = Gson() + val enterResultModel = MutableLiveData() + val outResultModel = MutableLiveData() + + fun enter(sid: String, account: String, secretKey: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.login(sid, account, secretKey) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + "登录成功".show(BaseApplication.obtainInstance()) + enterResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) + + fun out() = launch({ + val response = RetrofitServiceManager.loginOut() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + outResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt new file mode 100644 index 0000000..241d074 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.model.UserDetailModel +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.BaseViewModel + +class UserViewModel : BaseViewModel() { + + private val gson = Gson() + val userDetailModel = MutableLiveData() + + fun obtainUserDetail() = launch({ + val response = RetrofitServiceManager.obtainUserDetail() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + val userDetail = gson.fromJson( + response, object : TypeToken() {}.type + ) + userDetailModel.value = userDetail + SaveKeyValues.putValue(LocalConstant.USER_DETAIL_MODEL, gson.toJson(userDetail.data)) + } else { + val errorModel = UserDetailModel() + errorModel.code = responseCode + userDetailModel.value = errorModel + //如果此次获取不到用户信息,那么就清空之前的用户缓存,然后让用户重新登录 + SaveKeyValues.removeKey(LocalConstant.USER_DETAIL_MODEL) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7ad6379 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.PublicKeyModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel + +class AuthenticateViewModel : BaseViewModel() { + + private val gson = Gson() + val keyModel = MutableLiveData() + + fun obtainPublicKey() = launch({ + val response = RetrofitServiceManager.authenticate() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + keyModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt new file mode 100644 index 0000000..5211816 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt @@ -0,0 +1,55 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.model.LoginResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class LoginViewModel : BaseViewModel() { + + private val gson = Gson() + val enterResultModel = MutableLiveData() + val outResultModel = MutableLiveData() + + fun enter(sid: String, account: String, secretKey: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.login(sid, account, secretKey) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + "登录成功".show(BaseApplication.obtainInstance()) + enterResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) + + fun out() = launch({ + val response = RetrofitServiceManager.loginOut() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + outResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt new file mode 100644 index 0000000..241d074 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.model.UserDetailModel +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.BaseViewModel + +class UserViewModel : BaseViewModel() { + + private val gson = Gson() + val userDetailModel = MutableLiveData() + + fun obtainUserDetail() = launch({ + val response = RetrofitServiceManager.obtainUserDetail() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + val userDetail = gson.fromJson( + response, object : TypeToken() {}.type + ) + userDetailModel.value = userDetail + SaveKeyValues.putValue(LocalConstant.USER_DETAIL_MODEL, gson.toJson(userDetail.data)) + } else { + val errorModel = UserDetailModel() + errorModel.code = responseCode + userDetailModel.value = errorModel + //如果此次获取不到用户信息,那么就清空之前的用户缓存,然后让用户重新登录 + SaveKeyValues.removeKey(LocalConstant.USER_DETAIL_MODEL) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_camera.xml b/app/src/main/res/drawable/ic_camera.xml new file mode 100644 index 0000000..b612ce9 --- /dev/null +++ b/app/src/main/res/drawable/ic_camera.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7ad6379 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.PublicKeyModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel + +class AuthenticateViewModel : BaseViewModel() { + + private val gson = Gson() + val keyModel = MutableLiveData() + + fun obtainPublicKey() = launch({ + val response = RetrofitServiceManager.authenticate() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + keyModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt new file mode 100644 index 0000000..5211816 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt @@ -0,0 +1,55 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.model.LoginResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class LoginViewModel : BaseViewModel() { + + private val gson = Gson() + val enterResultModel = MutableLiveData() + val outResultModel = MutableLiveData() + + fun enter(sid: String, account: String, secretKey: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.login(sid, account, secretKey) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + "登录成功".show(BaseApplication.obtainInstance()) + enterResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) + + fun out() = launch({ + val response = RetrofitServiceManager.loginOut() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + outResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt new file mode 100644 index 0000000..241d074 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.model.UserDetailModel +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.BaseViewModel + +class UserViewModel : BaseViewModel() { + + private val gson = Gson() + val userDetailModel = MutableLiveData() + + fun obtainUserDetail() = launch({ + val response = RetrofitServiceManager.obtainUserDetail() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + val userDetail = gson.fromJson( + response, object : TypeToken() {}.type + ) + userDetailModel.value = userDetail + SaveKeyValues.putValue(LocalConstant.USER_DETAIL_MODEL, gson.toJson(userDetail.data)) + } else { + val errorModel = UserDetailModel() + errorModel.code = responseCode + userDetailModel.value = errorModel + //如果此次获取不到用户信息,那么就清空之前的用户缓存,然后让用户重新登录 + SaveKeyValues.removeKey(LocalConstant.USER_DETAIL_MODEL) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_camera.xml b/app/src/main/res/drawable/ic_camera.xml new file mode 100644 index 0000000..b612ce9 --- /dev/null +++ b/app/src/main/res/drawable/ic_camera.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 0000000..52e376f --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7ad6379 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.PublicKeyModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel + +class AuthenticateViewModel : BaseViewModel() { + + private val gson = Gson() + val keyModel = MutableLiveData() + + fun obtainPublicKey() = launch({ + val response = RetrofitServiceManager.authenticate() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + keyModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt new file mode 100644 index 0000000..5211816 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt @@ -0,0 +1,55 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.model.LoginResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class LoginViewModel : BaseViewModel() { + + private val gson = Gson() + val enterResultModel = MutableLiveData() + val outResultModel = MutableLiveData() + + fun enter(sid: String, account: String, secretKey: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.login(sid, account, secretKey) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + "登录成功".show(BaseApplication.obtainInstance()) + enterResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) + + fun out() = launch({ + val response = RetrofitServiceManager.loginOut() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + outResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt new file mode 100644 index 0000000..241d074 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.model.UserDetailModel +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.BaseViewModel + +class UserViewModel : BaseViewModel() { + + private val gson = Gson() + val userDetailModel = MutableLiveData() + + fun obtainUserDetail() = launch({ + val response = RetrofitServiceManager.obtainUserDetail() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + val userDetail = gson.fromJson( + response, object : TypeToken() {}.type + ) + userDetailModel.value = userDetail + SaveKeyValues.putValue(LocalConstant.USER_DETAIL_MODEL, gson.toJson(userDetail.data)) + } else { + val errorModel = UserDetailModel() + errorModel.code = responseCode + userDetailModel.value = errorModel + //如果此次获取不到用户信息,那么就清空之前的用户缓存,然后让用户重新登录 + SaveKeyValues.removeKey(LocalConstant.USER_DETAIL_MODEL) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_camera.xml b/app/src/main/res/drawable/ic_camera.xml new file mode 100644 index 0000000..b612ce9 --- /dev/null +++ b/app/src/main/res/drawable/ic_camera.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 0000000..52e376f --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lock.xml b/app/src/main/res/drawable/ic_lock.xml new file mode 100644 index 0000000..f0a5610 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7ad6379 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.PublicKeyModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel + +class AuthenticateViewModel : BaseViewModel() { + + private val gson = Gson() + val keyModel = MutableLiveData() + + fun obtainPublicKey() = launch({ + val response = RetrofitServiceManager.authenticate() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + keyModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt new file mode 100644 index 0000000..5211816 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt @@ -0,0 +1,55 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.model.LoginResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class LoginViewModel : BaseViewModel() { + + private val gson = Gson() + val enterResultModel = MutableLiveData() + val outResultModel = MutableLiveData() + + fun enter(sid: String, account: String, secretKey: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.login(sid, account, secretKey) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + "登录成功".show(BaseApplication.obtainInstance()) + enterResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) + + fun out() = launch({ + val response = RetrofitServiceManager.loginOut() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + outResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt new file mode 100644 index 0000000..241d074 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.model.UserDetailModel +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.BaseViewModel + +class UserViewModel : BaseViewModel() { + + private val gson = Gson() + val userDetailModel = MutableLiveData() + + fun obtainUserDetail() = launch({ + val response = RetrofitServiceManager.obtainUserDetail() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + val userDetail = gson.fromJson( + response, object : TypeToken() {}.type + ) + userDetailModel.value = userDetail + SaveKeyValues.putValue(LocalConstant.USER_DETAIL_MODEL, gson.toJson(userDetail.data)) + } else { + val errorModel = UserDetailModel() + errorModel.code = responseCode + userDetailModel.value = errorModel + //如果此次获取不到用户信息,那么就清空之前的用户缓存,然后让用户重新登录 + SaveKeyValues.removeKey(LocalConstant.USER_DETAIL_MODEL) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_camera.xml b/app/src/main/res/drawable/ic_camera.xml new file mode 100644 index 0000000..b612ce9 --- /dev/null +++ b/app/src/main/res/drawable/ic_camera.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 0000000..52e376f --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lock.xml b/app/src/main/res/drawable/ic_lock.xml new file mode 100644 index 0000000..f0a5610 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_phone.xml b/app/src/main/res/drawable/ic_phone.xml new file mode 100644 index 0000000..831c4c4 --- /dev/null +++ b/app/src/main/res/drawable/ic_phone.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7ad6379 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.PublicKeyModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel + +class AuthenticateViewModel : BaseViewModel() { + + private val gson = Gson() + val keyModel = MutableLiveData() + + fun obtainPublicKey() = launch({ + val response = RetrofitServiceManager.authenticate() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + keyModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt new file mode 100644 index 0000000..5211816 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt @@ -0,0 +1,55 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.model.LoginResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class LoginViewModel : BaseViewModel() { + + private val gson = Gson() + val enterResultModel = MutableLiveData() + val outResultModel = MutableLiveData() + + fun enter(sid: String, account: String, secretKey: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.login(sid, account, secretKey) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + "登录成功".show(BaseApplication.obtainInstance()) + enterResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) + + fun out() = launch({ + val response = RetrofitServiceManager.loginOut() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + outResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt new file mode 100644 index 0000000..241d074 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.model.UserDetailModel +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.BaseViewModel + +class UserViewModel : BaseViewModel() { + + private val gson = Gson() + val userDetailModel = MutableLiveData() + + fun obtainUserDetail() = launch({ + val response = RetrofitServiceManager.obtainUserDetail() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + val userDetail = gson.fromJson( + response, object : TypeToken() {}.type + ) + userDetailModel.value = userDetail + SaveKeyValues.putValue(LocalConstant.USER_DETAIL_MODEL, gson.toJson(userDetail.data)) + } else { + val errorModel = UserDetailModel() + errorModel.code = responseCode + userDetailModel.value = errorModel + //如果此次获取不到用户信息,那么就清空之前的用户缓存,然后让用户重新登录 + SaveKeyValues.removeKey(LocalConstant.USER_DETAIL_MODEL) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_camera.xml b/app/src/main/res/drawable/ic_camera.xml new file mode 100644 index 0000000..b612ce9 --- /dev/null +++ b/app/src/main/res/drawable/ic_camera.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 0000000..52e376f --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lock.xml b/app/src/main/res/drawable/ic_lock.xml new file mode 100644 index 0000000..f0a5610 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_phone.xml b/app/src/main/res/drawable/ic_phone.xml new file mode 100644 index 0000000..831c4c4 --- /dev/null +++ b/app/src/main/res/drawable/ic_phone.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_storage.xml b/app/src/main/res/drawable/ic_storage.xml new file mode 100644 index 0000000..898c1cf --- /dev/null +++ b/app/src/main/res/drawable/ic_storage.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7ad6379 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.PublicKeyModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel + +class AuthenticateViewModel : BaseViewModel() { + + private val gson = Gson() + val keyModel = MutableLiveData() + + fun obtainPublicKey() = launch({ + val response = RetrofitServiceManager.authenticate() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + keyModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt new file mode 100644 index 0000000..5211816 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt @@ -0,0 +1,55 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.model.LoginResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class LoginViewModel : BaseViewModel() { + + private val gson = Gson() + val enterResultModel = MutableLiveData() + val outResultModel = MutableLiveData() + + fun enter(sid: String, account: String, secretKey: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.login(sid, account, secretKey) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + "登录成功".show(BaseApplication.obtainInstance()) + enterResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) + + fun out() = launch({ + val response = RetrofitServiceManager.loginOut() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + outResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt new file mode 100644 index 0000000..241d074 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.model.UserDetailModel +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.BaseViewModel + +class UserViewModel : BaseViewModel() { + + private val gson = Gson() + val userDetailModel = MutableLiveData() + + fun obtainUserDetail() = launch({ + val response = RetrofitServiceManager.obtainUserDetail() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + val userDetail = gson.fromJson( + response, object : TypeToken() {}.type + ) + userDetailModel.value = userDetail + SaveKeyValues.putValue(LocalConstant.USER_DETAIL_MODEL, gson.toJson(userDetail.data)) + } else { + val errorModel = UserDetailModel() + errorModel.code = responseCode + userDetailModel.value = errorModel + //如果此次获取不到用户信息,那么就清空之前的用户缓存,然后让用户重新登录 + SaveKeyValues.removeKey(LocalConstant.USER_DETAIL_MODEL) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_camera.xml b/app/src/main/res/drawable/ic_camera.xml new file mode 100644 index 0000000..b612ce9 --- /dev/null +++ b/app/src/main/res/drawable/ic_camera.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 0000000..52e376f --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lock.xml b/app/src/main/res/drawable/ic_lock.xml new file mode 100644 index 0000000..f0a5610 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_phone.xml b/app/src/main/res/drawable/ic_phone.xml new file mode 100644 index 0000000..831c4c4 --- /dev/null +++ b/app/src/main/res/drawable/ic_phone.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_storage.xml b/app/src/main/res/drawable/ic_storage.xml new file mode 100644 index 0000000..898c1cf --- /dev/null +++ b/app/src/main/res/drawable/ic_storage.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_user.xml b/app/src/main/res/drawable/ic_user.xml new file mode 100644 index 0000000..8c4a29d --- /dev/null +++ b/app/src/main/res/drawable/ic_user.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/build.gradle b/app/build.gradle index a2ebaeb..ec40a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,9 @@ implementation 'pub.devrel:easypermissions:3.0.0' //沉浸式状态栏。基础依赖包,必须要依赖 implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + //Loading框 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' //MVVM+LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index 0999d5f..07fbb2e 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96992be..135b8bc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt index 4ee1561..de0d0b9 100644 --- a/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/smarttube/base/BaseApplication.kt @@ -2,11 +2,19 @@ import android.app.Application import com.pengxh.kt.lite.utils.SaveKeyValues +import kotlin.properties.Delegates class BaseApplication : Application() { + companion object { + private var instance: BaseApplication by Delegates.notNull() + + fun obtainInstance() = instance + } + override fun onCreate() { super.onCreate() + instance = this SaveKeyValues.initSharedPreferences(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/extensions/String.kt b/app/src/main/java/com/casic/smarttube/extensions/String.kt new file mode 100644 index 0000000..e585cca --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/extensions/String.kt @@ -0,0 +1,35 @@ +package com.casic.smarttube.extensions + +import com.casic.smarttube.model.ErrorMessageModel +import com.casic.smarttube.utils.LocalConstant +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.utils.SaveKeyValues +import org.json.JSONObject +import java.util.* + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + return JSONObject(this).getInt("code") +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +//拼接图片地址 +fun String.combineImagePath(): String { + if (this.isEmpty()) return this + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return "$defaultValue/static/${this.replace("\\", "/")}" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java new file mode 100644 index 0000000..eecfd74 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/CommonResultModel.java @@ -0,0 +1,43 @@ +package com.casic.smarttube.model; + +/** + * 普通实体类,失败/成功数据结构一致 + */ +public class CommonResultModel { + private int code; + private String data; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java new file mode 100644 index 0000000..411674a --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.smarttube.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java new file mode 100644 index 0000000..6497254 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/LoginResultModel.java @@ -0,0 +1,62 @@ +package com.casic.smarttube.model; + +public class LoginResultModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private String kaptcha; + private String token; + + public String getKaptcha() { + return kaptcha; + } + + public void setKaptcha(String kaptcha) { + this.kaptcha = kaptcha; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java new file mode 100644 index 0000000..d440bc8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/PublicKeyModel.java @@ -0,0 +1,80 @@ +package com.casic.smarttube.model; + +public class PublicKeyModel { + + private int code; + private DataBean data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataBean getData() { + return data; + } + + public void setData(DataBean data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataBean { + private boolean appKaptcha; + private boolean kaptcha; + private String publicKey; + private String sid; + + public boolean isAppKaptcha() { + return appKaptcha; + } + + public void setAppKaptcha(boolean appKaptcha) { + this.appKaptcha = appKaptcha; + } + + public boolean isKaptcha() { + return kaptcha; + } + + public void setKaptcha(boolean kaptcha) { + this.kaptcha = kaptcha; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java new file mode 100644 index 0000000..149bec8 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/model/UserDetailModel.java @@ -0,0 +1,227 @@ +package com.casic.smarttube.model; + + +import java.util.List; + +public class UserDetailModel { + private int code; + private Data data; + private String message; + private boolean success; + + public void setCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public void setData(Data data) { + this.data = data; + } + + public Data getData() { + return data; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean getSuccess() { + return success; + } + + public static class Data { + + private String account; + private String attr1; + private String avatar; + private String bizData; + private List dataScope; + private String deptId; + private String deptName; + private List devices; + private String id; + private String ipAddr; + private String name; + private String phone; + private List roleList; + private List roleNames; + private List roleTips; + private String scopeType; + private String sysData; + private String targetId; + private String targetName; + private String tenantId; + + public void setAccount(String account) { + this.account = account; + } + + public String getAccount() { + return account; + } + + public void setAttr1(String attr1) { + this.attr1 = attr1; + } + + public String getAttr1() { + return attr1; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar; + } + + public void setBizData(String bizData) { + this.bizData = bizData; + } + + public String getBizData() { + return bizData; + } + + public void setDataScope(List dataScope) { + this.dataScope = dataScope; + } + + public List getDataScope() { + return dataScope; + } + + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + public String getDeptId() { + return deptId; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptName() { + return deptName; + } + + public void setDevices(List devices) { + this.devices = devices; + } + + public List getDevices() { + return devices; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPhone() { + return phone; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } + + public List getRoleList() { + return roleList; + } + + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + + public List getRoleNames() { + return roleNames; + } + + public void setRoleTips(List roleTips) { + this.roleTips = roleTips; + } + + public List getRoleTips() { + return roleTips; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeType() { + return scopeType; + } + + public void setSysData(String sysData) { + this.sysData = sysData; + } + + public String getSysData() { + return sysData; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public String getTargetName() { + return targetName; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantId() { + return tenantId; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..b44fec6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.smarttube.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String? + get() = SaveKeyValues.getValue("keyString", "") as String? + + fun saveToken(token: String?) { + SaveKeyValues.putValue("token", token!!) + } + + val token: String? + get() = SaveKeyValues.getValue("token", "") as String? + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/Constant.kt b/app/src/main/java/com/casic/smarttube/utils/Constant.kt deleted file mode 100644 index d921387..0000000 --- a/app/src/main/java/com/casic/smarttube/utils/Constant.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.casic.smarttube.utils - -import android.Manifest - - -object Constant { - val USER_PERMISSIONS = arrayOf( - Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, - Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE - ) - - const val PERMISSIONS_CODE = 999 -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt new file mode 100644 index 0000000..bb97c01 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/DialogHelper.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils; + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object DialogHelper { + private lateinit var loadingDialog: QMUITipDialog + + fun showLoadingDialog(activity: Activity, message: String?) { + loadingDialog = QMUITipDialog.Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismissLoadingDialog() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt new file mode 100644 index 0000000..cee379c --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocalConstant.kt @@ -0,0 +1,43 @@ +package com.casic.smarttube.utils + +import android.Manifest + + +object LocalConstant { + /** + * ============================================================================================= + * Array + * ============================================================================================= + * */ + val USER_PERMISSIONS = arrayOf( + Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + ) + + /** + * ============================================================================================= + * Int + * ============================================================================================= + * */ + const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * Long + * ============================================================================================= + * */ + + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val USER_DETAIL_MODEL = "userDetailModel" + const val SERVER_BASE_URL = "http://111.198.10.15:11304" + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val ACCOUNT = "account" + const val PASSWORD = "password" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt new file mode 100644 index 0000000..14e2f56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.smarttube.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 + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_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) + 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/smarttube/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..660496f --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,47 @@ +package com.casic.smarttube.utils.retrofit; + +import android.util.Log +import com.casic.smarttube.utils.LocalConstant +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import com.pengxh.kt.lite.utils.SaveKeyValues +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 kTag = "RetrofitFactory" + + private val httpClient: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + LocalConstant.DEFAULT_SERVER_CONFIG, LocalConstant.SERVER_BASE_URL + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(httpClient) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(kTag, ">>>>> $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..6dad7b3 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitService.kt @@ -0,0 +1,52 @@ +package com.casic.smarttube.utils.retrofit + +import okhttp3.MultipartBody +import retrofit2.http.* + + +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/config/baseConfig") + suspend fun obtainPublicKey(): String + + /** + * 登录并获取Token + * + * @param sid + * @param account 用户名 + * @param secretKey 加密后的密码 + */ + @FormUrlEncoded + @POST("/user/login") + suspend fun obtainLoginResult( + @Field("sid") sid: String, + @Field("username") account: String, + @Field("password") secretKey: String + ): String + + /** + * 退出登录 + */ + @GET("/user/logout") + suspend fun loginOut(@Header("token") token: String): String + + /** + * 获取用户信息 + */ + @GET("/user/info") + suspend fun obtainUserDetail(@Header("token") token: String): String + + /** + * 上传图片 + * 系统路径static拼接图片返回路径 + * http://xx.com/static/2019-10/8050891248624f2bbefedcb196ce89cb.jpeg + */ + @Multipart + @POST("/imageUpload") + suspend fun uploadImage( + @Header("token") token: String, + @Part file: MultipartBody.Part + ): String +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..db99cfb --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,50 @@ +package com.casic.smarttube.utils.retrofit + +import com.casic.smarttube.utils.AuthenticationHelper +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): String { + return api.obtainPublicKey() + } + + /** + * 登录并获取Token + */ + suspend fun login(sid: String, account: String, secretKey: String): String { + return api.obtainLoginResult(sid, account, secretKey) + } + + /** + * 退出登录 + */ + suspend fun loginOut(): String { + return api.loginOut(AuthenticationHelper.token!!) + } + + /** + * 获取用户信息 + */ + suspend fun obtainUserDetail(): String { + return api.obtainUserDetail(AuthenticationHelper.token!!) + } + + /** + * 上传图片 + */ + suspend fun uploadImage(image: File): String { + val requestBody = RequestBody.create("image/png".toMediaTypeOrNull(), image) + val imagePart = MultipartBody.Part.createFormData("file", image.name, requestBody) + return api.uploadImage(AuthenticationHelper.token!!, imagePart) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt new file mode 100644 index 0000000..aeccd80 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/LoginActivity.kt @@ -0,0 +1,134 @@ +package com.casic.smarttube.view + +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 androidx.lifecycle.ViewModelProvider +import com.casic.smarttube.R +import com.casic.smarttube.utils.AuthenticationHelper +import com.casic.smarttube.utils.DialogHelper +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.RSAUtils +import com.casic.smarttube.vm.AuthenticateViewModel +import com.casic.smarttube.vm.LoginViewModel +import com.casic.smarttube.vm.UserViewModel +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.LoadState +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : KotlinBaseActivity() { + + private lateinit var authenticateViewModel: AuthenticateViewModel + private lateinit var loginViewModel: LoginViewModel + private lateinit var userViewModel: UserViewModel + private val textWatcher = 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 > 16) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + } + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + } + + override fun initData() { + // 设置默认账号密码 + userNameView.setText(SaveKeyValues.getValue(LocalConstant.ACCOUNT, "") as String) + userPasswordView.setText(SaveKeyValues.getValue(LocalConstant.PASSWORD, "") as String) + authenticateViewModel = ViewModelProvider(this).get(AuthenticateViewModel::class.java) + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + userViewModel = ViewModelProvider(this).get(UserViewModel::class.java) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(textWatcher) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + authenticateViewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setOnClickListener { + val account = userNameView.text.toString().trim() + val userPassword = userPasswordView.text.toString().trim() + if (account.isBlank()) { + "用户名不能为空".show(this) + return@setOnClickListener + } + if (userPassword.isBlank()) { + "密码不能为空".show(this) + return@setOnClickListener + } + SaveKeyValues.putValue(LocalConstant.ACCOUNT, account) + SaveKeyValues.putValue(LocalConstant.PASSWORD, userPassword) + authenticateViewModel.obtainPublicKey() + } + authenticateViewModel.keyModel.observe(this, { + if (it.code == 200) {//用code判断,别的判断可能有坑 + val keyString = it.data!!.publicKey!! + /** + * 修改密码需要用到key,先存着 + * */ + AuthenticationHelper.savePublicKey(keyString) + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + val dataByPublicKey = + RSAUtils.encryptDataByPublicKey(userPassword.toByteArray(), publicKey!!) + //登录并获取Token,POST请求 + loginViewModel.enter(it.data!!.sid!!, account, dataByPublicKey) + loginViewModel.enterResultModel.observe(this, { loginResult -> + if (loginResult.code == 200) { + AuthenticationHelper.saveToken(loginResult.data!!.token!!) + /** + * 获取token之后保存用户信息 + * */ + userViewModel.obtainUserDetail() + //验证成功登录 + this.navigatePageTo() + finish() + } + }) + loginViewModel.loadState.observe(this, { loginState -> + when (loginState) { + is LoadState.Loading -> { + DialogHelper.showLoadingDialog(this, "登录中,请稍后") + } + is LoadState.Success -> { + DialogHelper.dismissLoadingDialog() + } + else -> { + DialogHelper.dismissLoadingDialog() + } + } + }) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt index c05f04a..d6dee4d 100644 --- a/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/PermissionActivity.kt @@ -1,32 +1,46 @@ package com.casic.smarttube.view -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import com.amap.api.navi.NaviSetting -import com.casic.smarttube.utils.Constant +import com.casic.smarttube.R +import com.casic.smarttube.utils.LocalConstant +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.navigatePageTo -import com.pengxh.kt.lite.utils.PageNavigationManager +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_permssion.* import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { +class PermissionActivity : KotlinBaseActivity(), PermissionCallbacks { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - PageNavigationManager.addActivity(this) + override fun initLayoutView(): Int = R.layout.activity_permssion + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + ImmerseStatusBarUtil.setColor(this, R.color.white.convertColor(this)) + } + + override fun initData() { //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + if (EasyPermissions.hasPermissions(this, *LocalConstant.USER_PERMISSIONS)) { startSplashScreenActivity() } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) + enterMainButton.setOnClickListener { + EasyPermissions.requestPermissions( + this@PermissionActivity, + resources.getString(R.string.app_name) + "需要获取存储相关权限", + LocalConstant.PERMISSIONS_CODE, + *LocalConstant.USER_PERMISSIONS + ) + } } } + override fun initEvent() { + + } + private fun startSplashScreenActivity() { //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) @@ -36,9 +50,7 @@ } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) diff --git a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt index ba7ef6c..5b00fc6 100644 --- a/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt +++ b/app/src/main/java/com/casic/smarttube/view/SplashScreenActivity.kt @@ -1,11 +1,39 @@ package com.casic.smarttube.view +import android.annotation.SuppressLint +import android.os.CountDownTimer +import androidx.lifecycle.ViewModelProvider import com.casic.smarttube.R +import com.casic.smarttube.vm.UserViewModel import com.gyf.immersionbar.ImmersionBar import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +@SuppressLint("CustomSplashScreen") class SplashScreenActivity : KotlinBaseActivity() { + private lateinit var userDetailViewModel: UserViewModel + private val countDownTimer = object : CountDownTimer(1000, 500) { + override fun onFinish() { + /** + * 获取token之后保存用户信息 + * */ + userDetailViewModel.obtainUserDetail() + userDetailViewModel.userDetailModel.observe(this@SplashScreenActivity, { + if (it.code == 200) { + navigatePageTo() + } else { + navigatePageTo() + } + finish() + }) + } + + override fun onTick(millisUntilFinished: Long) { + + } + } + override fun initLayoutView(): Int = R.layout.activity_splash override fun setupTopBarLayout() { @@ -13,10 +41,10 @@ } override fun initData() { - + userDetailViewModel = ViewModelProvider(this).get(UserViewModel::class.java) } override fun initEvent() { - + countDownTimer.start() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7ad6379 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/AuthenticateViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.PublicKeyModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel + +class AuthenticateViewModel : BaseViewModel() { + + private val gson = Gson() + val keyModel = MutableLiveData() + + fun obtainPublicKey() = launch({ + val response = RetrofitServiceManager.authenticate() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + keyModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt new file mode 100644 index 0000000..5211816 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/LoginViewModel.kt @@ -0,0 +1,55 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.model.LoginResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class LoginViewModel : BaseViewModel() { + + private val gson = Gson() + val enterResultModel = MutableLiveData() + val outResultModel = MutableLiveData() + + fun enter(sid: String, account: String, secretKey: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.login(sid, account, secretKey) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + "登录成功".show(BaseApplication.obtainInstance()) + enterResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) + + fun out() = launch({ + val response = RetrofitServiceManager.loginOut() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + outResultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt new file mode 100644 index 0000000..241d074 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UserViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.model.UserDetailModel +import com.casic.smarttube.utils.LocalConstant +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.utils.SaveKeyValues +import com.pengxh.kt.lite.vm.BaseViewModel + +class UserViewModel : BaseViewModel() { + + private val gson = Gson() + val userDetailModel = MutableLiveData() + + fun obtainUserDetail() = launch({ + val response = RetrofitServiceManager.obtainUserDetail() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + val userDetail = gson.fromJson( + response, object : TypeToken() {}.type + ) + userDetailModel.value = userDetail + SaveKeyValues.putValue(LocalConstant.USER_DETAIL_MODEL, gson.toJson(userDetail.data)) + } else { + val errorModel = UserDetailModel() + errorModel.code = responseCode + userDetailModel.value = errorModel + //如果此次获取不到用户信息,那么就清空之前的用户缓存,然后让用户重新登录 + SaveKeyValues.removeKey(LocalConstant.USER_DETAIL_MODEL) + } + }, { + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_camera.xml b/app/src/main/res/drawable/ic_camera.xml new file mode 100644 index 0000000..b612ce9 --- /dev/null +++ b/app/src/main/res/drawable/ic_camera.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 0000000..52e376f --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lock.xml b/app/src/main/res/drawable/ic_lock.xml new file mode 100644 index 0000000..f0a5610 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_phone.xml b/app/src/main/res/drawable/ic_phone.xml new file mode 100644 index 0000000..831c4c4 --- /dev/null +++ b/app/src/main/res/drawable/ic_phone.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_storage.xml b/app/src/main/res/drawable/ic_storage.xml new file mode 100644 index 0000000..898c1cf --- /dev/null +++ b/app/src/main/res/drawable/ic_storage.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_user.xml b/app/src/main/res/drawable/ic_user.xml new file mode 100644 index 0000000..8c4a29d --- /dev/null +++ b/app/src/main/res/drawable/ic_user.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..e788dbc --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + +