Преглед на файлове

@task: Login and Project

bai преди 3 години
родител
ревизия
2a5d14d44b
променени са 30 файла, в които са добавени 814 реда и са изтрити 97 реда
  1. 30 31
      demo/build.gradle
  2. 11 4
      demo/src/main/AndroidManifest.xml
  3. 13 2
      demo/src/main/java/com/framework/app/api/Api.kt
  4. 2 2
      demo/src/main/java/com/framework/app/tools/Md5.kt
  5. 33 7
      demo/src/main/java/com/framework/mvvm/model/data/DataSource.kt
  6. 5 5
      demo/src/main/java/com/framework/mvvm/model/data/Http.kt
  7. 1 0
      demo/src/main/java/com/framework/mvvm/model/data/Model.kt
  8. 3 1
      demo/src/main/java/com/framework/mvvm/model/db/AdmDatabase.kt
  9. 6 2
      demo/src/main/java/com/framework/mvvm/model/db/dao/ProjectDao.kt
  10. 30 0
      demo/src/main/java/com/framework/mvvm/model/db/dao/UserDao.kt
  11. 13 12
      demo/src/main/java/com/framework/mvvm/model/db/entity/ProjectEntity.kt
  12. 20 0
      demo/src/main/java/com/framework/mvvm/model/db/entity/UserEntity.kt
  13. 108 8
      demo/src/main/java/com/framework/mvvm/model/repository/AdmRepository.kt
  14. 1 1
      demo/src/main/java/com/framework/mvvm/model/vo/Client.kt
  15. 11 0
      demo/src/main/java/com/framework/mvvm/model/vo/Login.kt
  16. 17 2
      demo/src/main/java/com/framework/mvvm/model/vo/User.kt
  17. 37 6
      demo/src/main/java/com/framework/mvvm/mv/AdmViewModel.kt
  18. 33 0
      demo/src/main/java/com/framework/mvvm/ui/AdmApp.kt
  19. 48 0
      demo/src/main/java/com/framework/mvvm/ui/LoginActivity.kt
  20. 82 0
      demo/src/main/java/com/framework/mvvm/ui/screen/FlashScreen.kt
  21. 132 0
      demo/src/main/java/com/framework/mvvm/ui/screen/LoginScreen.kt
  22. 74 0
      demo/src/main/java/com/framework/mvvm/ui/screen/ProjectScreen.kt
  23. 2 3
      demo/src/main/java/com/sybotan/android/demo/DemoApp.kt
  24. 2 11
      demo/src/main/java/com/sybotan/android/demo/activities/PocActivity.kt
  25. 9 0
      demo/src/main/java/com/sybotan/android/demo/ui/theme/Color.kt
  26. 11 0
      demo/src/main/java/com/sybotan/android/demo/ui/theme/Shape.kt
  27. 45 0
      demo/src/main/java/com/sybotan/android/demo/ui/theme/Theme.kt
  28. 34 0
      demo/src/main/java/com/sybotan/android/demo/ui/theme/Type.kt
  29. BIN
      demo/src/main/res/drawable/logo.png
  30. 1 0
      demo/src/main/res/values/strings.xml

+ 30 - 31
demo/build.gradle

@@ -1,29 +1,3 @@
-/*
- * ********************************************************************************************************************
- *
- *               iFHS7.
- *              ;BBMBMBMc                  rZMBMBR              BMB
- *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
- *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
- *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
- *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
- *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
- *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
- *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
- *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
- *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
- *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
- *         i0BM: SWKHKGO  MBDv
- *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
- *              ,  XMW  ..
- *                  r                                                                     All rights reserved.
- *
- * ********************************************************************************************************************
- */
-
-/*apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'*/
 plugins {
     id 'com.android.application'
     id 'kotlin-android'
@@ -103,6 +77,9 @@ android {
         exclude 'META-INF/license.txt'
         exclude 'META-INF/dependencies.txt'
         exclude 'META-INF/LGPL2.1'
+        resources {
+            excludes += '/META-INF/{AL2.0,LGPL2.1}'
+        }
     }
 
     compileOptions {
@@ -120,6 +97,12 @@ android {
         cleanedAnnotations
         compile.exclude group: 'com.intellij', module: 'annotations'
     }
+    buildFeatures {
+        compose true
+    }
+    composeOptions {
+        kotlinCompilerExtensionVersion "1.1.0-beta01"
+    }
 }
 
 
@@ -129,7 +112,19 @@ dependencies {
     implementation files('libs/universal-image-loader-1.9.4-with-sources.jar')
     implementation files('libs/httpclient-4.3.5.jar')
     implementation files('libs/httpcore-4.3.2.jar')
-    implementation 'com.google.ar:core:1.23.0'
+    implementation 'com.google.ar:core:1.25.0'
+
+    implementation "androidx.compose.animation:animation:1.1.0-beta03"
+    implementation "androidx.compose.compiler:compiler:1.1.0-beta03"
+    implementation "androidx.compose.foundation:foundation:1.1.0-beta03"
+    implementation "androidx.compose.runtime:runtime:1.1.0-beta03"
+    implementation "androidx.compose.ui:ui:1.1.0-beta03"
+    implementation 'androidx.compose.material:material:1.1.0-beta03'
+    implementation "androidx.navigation:navigation-compose:2.4.0-beta02"
+    implementation "androidx.compose.ui:ui-tooling-preview:1.1.0-beta03"
+    implementation 'androidx.activity:activity-compose:1.3.1'
+    implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-rc02"
+
     testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: JUNIT_VERSION
     androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
         exclude group: 'com.android.support', module: 'support-annotations'
@@ -153,6 +148,9 @@ dependencies {
 
     implementation 'com.github.bumptech.glide:glide:4.0.0'
     implementation 'com.github.bumptech.glide:okhttp3-integration:4.0.0'
+    androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.1.0-beta02"
+    debugImplementation "androidx.compose.ui:ui-tooling:1.1.0-beta02"
+    debugImplementation "androidx.compose.ui:ui-test-manifest:1.1.0-beta02"
     kapt 'com.github.bumptech.glide:compiler:4.0.0'
     implementation 'com.contrarywind:Android-PickerView:4.1.3'
     implementation 'com.bm.photoview:library:1.4.1'
@@ -236,12 +234,13 @@ dependencies {
     implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
     implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
 
-    implementation 'org.kodein.di:kodein-di:7.8.0'
+    implementation 'org.kodein.di:kodein-di:7.9.0'
 
     //implementation 'org.kodein.di:kodein-di-framework-android-support:7.8.0'
-    implementation 'org.kodein.di:kodein-di-framework-android-core:7.8.0'
-    implementation 'org.kodein.di:kodein-di-framework-android-x:7.8.0'
-    implementation 'org.kodein.di:kodein-di-framework-android-x-viewmodel:7.8.0'
+    implementation 'org.kodein.di:kodein-di-framework-android-core:7.9.0'
+    implementation 'org.kodein.di:kodein-di-framework-android-x:7.9.0'
+    implementation 'org.kodein.di:kodein-di-framework-android-x-viewmodel:7.9.0'
+    implementation 'org.kodein.di:kodein-di-framework-compose:7.9.0'
 
     implementation 'com.arashivision.sdk:sdkcamera:1.3.10'
     implementation 'com.arashivision.sdk:sdkmedia:1.3.10'

+ 11 - 4
demo/src/main/AndroidManifest.xml

@@ -38,6 +38,17 @@
         android:supportsRtl="true"
         android:theme="@style/AppTheme">
         <activity
+            android:name="com.framework.mvvm.ui.LoginActivity"
+            android:exported="true"
+            android:label="@string/title_activity_login"
+            android:theme="@style/AppTheme" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
             android:name=".activities.SpaceDeliveryCodeActivity"
             android:exported="true" />
         <activity
@@ -73,11 +84,7 @@
         <activity
             android:name=".activities.PocActivity"
             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
 
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
         </activity>
         <activity
             android:name=".activities.poc.RecordInfoActivity"

+ 13 - 2
demo/src/main/java/com/framework/app/api/Api.kt

@@ -42,7 +42,7 @@ interface Api {
 
 
     @POST("adm/clientId")
-    suspend fun getClient(@Body user: User): Model<Client>
+    suspend fun getClient(@Body client: Client): Model<Client>
 
     /**
      * 获取文件上传接口
@@ -82,8 +82,19 @@ interface Api {
      * 返回地图
      */
     @GET
-    fun downloadFile(@Url url: String): String
+    suspend fun downloadFile(@Url url: String): String
 
+    /**
+     * 登录
+     */
+    @POST("http://82.157.151.123/login-service")
+    suspend fun login(@Query("jsonString") json: String): Model<List<User>>
+
+    /**
+     * 获取项目
+     */
+    @POST("http://82.157.151.123/getUserById")
+    suspend fun getProjects(@Query("jsonString") json: String): Model<List<User>>
 
 }
 

+ 2 - 2
demo/src/main/java/com/framework/app/tools/Md5.kt

@@ -31,8 +31,8 @@ fun fileMD5(file: File): String {
 /**
  * 字符串 md5
  */
-fun strMD5(string: String): String {
-    return md5(string.toByteArray())
+fun String.md5(): String {
+    return md5(this.toByteArray())
 }
 
 fun streamMD5(stream: InputStream): String {

+ 33 - 7
demo/src/main/java/com/framework/mvvm/model/data/DataSource.kt

@@ -1,10 +1,6 @@
 package com.framework.mvvm.model.data
 
-import androidx.annotation.MainThread
-import androidx.lifecycle.*
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.flow.*
 
 
 /**
@@ -20,6 +16,34 @@ import kotlinx.coroutines.withContext
  * @param  <ResultType> : 最终数据返回类型的封装
  *
  */
+
+inline fun <ResultType, RequestType> dataSource(
+    crossinline fromDb:  () -> Flow<ResultType>,
+    crossinline fromNt: suspend () -> RequestType,
+    crossinline onSuccess: suspend (RequestType) -> Unit,
+    crossinline onFailure: (Throwable) -> Unit = {},
+    crossinline isFromNt: (ResultType) -> Boolean = { true }
+) = flow<Data<ResultType>> {
+    emit(Data.loading())
+
+    val data = fromDb().firstOrNull()
+    val isNt = data == null || isFromNt(data)
+
+    val flow = if (!isNt) {
+        fromDb().map { Data.success(it) }
+    } else {
+        try {
+            onSuccess(fromNt())
+            fromDb().map { Data.success(it) }
+        } catch (t: Throwable) {
+            onFailure(t)
+            fromDb().map { Data.failure(t.message ?: "") }
+        }
+    }
+
+    emitAll(flow)
+}
+/*
 fun <RequestType, ResultType> dataSource(
     fromDb: suspend () -> LiveData<ResultType>,
     fromNt: suspend () -> Response<RequestType>,
@@ -37,9 +61,11 @@ fun <RequestType, ResultType> dataSource(
 }
 
 
+*/
 /**
  * 数据源
- */
+ *//*
+
 private class DataSource<RequestType, ResultType> @MainThread constructor(
     private val fromDb: suspend () -> LiveData<ResultType>,          // 磁盘获取数据
     private val fromNt: suspend () -> Response<RequestType>,      // 远程获取数据
@@ -101,4 +127,4 @@ private class DataSource<RequestType, ResultType> @MainThread constructor(
             removeObserver(observer)
         }
     }
-}
+}*/

+ 5 - 5
demo/src/main/java/com/framework/mvvm/model/data/Http.kt

@@ -9,19 +9,19 @@ suspend fun <T> request(
     from: suspend () -> Model<T>,
     to: suspend (T) -> Unit,
     error: ((String) -> Unit)? = null
-): Boolean {
-    var result = false
+) {
     runCatching {
         from()
     }.onSuccess {
-        if (it.code == "00000") {
+        if (it.code == "00000" || it.message == "success") {
             to(it.data)
-            result = true
+        } else {
+            Response.create<String>(it.message ?: "http request Ok, but don't have data")
         }
     }.onFailure {
         error?.invoke(it.message ?: "you know nothing")
     }
-    return result
+
 }
 
 /**

+ 1 - 0
demo/src/main/java/com/framework/mvvm/model/data/Model.kt

@@ -9,5 +9,6 @@ data class Model<T>(
     val code: String,
     @SerializedName(value = "message", alternate = ["result"])
     val message: String? = null,
+    @SerializedName(value = "data", alternate = ["content"])
     val data: T
 )

+ 3 - 1
demo/src/main/java/com/framework/mvvm/model/db/AdmDatabase.kt

@@ -34,7 +34,8 @@ import com.framework.mvvm.model.db.entity.task.*
         PipeEntity::class,
         ServeAreaEntity::class,
         ContainerConfigEntity::class,
-        InfoEntity::class
+        InfoEntity::class,
+        UserEntity::class
     ], version = 1, exportSchema = true
 )
 @TypeConverters(GsonConverter::class)
@@ -61,6 +62,7 @@ abstract class AdmDatabase : RoomDatabase() {
     abstract fun serveAreaDao(): ServeAreaDao
     abstract fun containerConfigDao(): ContainerConfigDao
     abstract fun infoDao(): InfoDao
+    abstract fun userDao(): UserDao
 
     companion object {
         @Volatile

+ 6 - 2
demo/src/main/java/com/framework/mvvm/model/db/dao/ProjectDao.kt

@@ -9,14 +9,18 @@ import com.framework.mvvm.model.db.entity.ProjectEntity
  */
 @Dao
 interface ProjectDao {
+
     @Query("select * from project")
-    fun getProjects(): LiveData<List<ProjectEntity>>
+    suspend fun getProjects(): List<ProjectEntity>
 
     @Query("select * from project where id = :id")
     suspend fun getProjectById(id: String): ProjectEntity
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
-    suspend fun insProject(projects: List<ProjectEntity>)
+    suspend fun insProjects(projects: List<ProjectEntity>)
+
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    suspend fun insProject(vararg project: ProjectEntity)
 
     @Delete
     suspend fun delProject(project: ProjectEntity)

+ 30 - 0
demo/src/main/java/com/framework/mvvm/model/db/dao/UserDao.kt

@@ -0,0 +1,30 @@
+package com.framework.mvvm.model.db.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.framework.mvvm.model.db.entity.UserEntity
+
+@Dao
+interface UserDao {
+
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insUser(vararg: UserEntity): Long
+
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insUsers(users: List<UserEntity>): List<Long>
+
+    @Query("select * from user where user_id =:userId")
+    fun getUserByUserId(userId: String): UserEntity
+
+    @Query("select * from user where username =:username and password = :password")
+    suspend fun getUser(username: String, password: String): UserEntity?
+
+    @Query("select * from user")
+    suspend fun getUsers(): MutableList<UserEntity>
+
+    @Query("select * from user where state = :state")
+    suspend fun getUserByState(state: Int): UserEntity
+
+}

+ 13 - 12
demo/src/main/java/com/framework/mvvm/model/db/entity/ProjectEntity.kt

@@ -10,15 +10,16 @@ import com.google.gson.JsonElement
  */
 @Entity(tableName = "project")
 data class ProjectEntity(
-    @PrimaryKey val id: String,
-    @ColumnInfo(name = "name") val name: String,
-    @ColumnInfo(name = "valid") val valid: Int,
-    @ColumnInfo(name = "obj_type") val objType: String?,
-    @ColumnInfo(name = "class_code") val classCode: String?,
-    @ColumnInfo(name = "group_code") val groupCode: String?,
-    @ColumnInfo(name = "grouping") val grouping: Int?,
-    @ColumnInfo(name = "local_id") val localId: String?,
-    @ColumnInfo(name = "local_name") val localName: String?,
-    @ColumnInfo(name = "infos") val infos: JsonElement? = null
-)
-
+    @PrimaryKey
+    @ColumnInfo(name = "id") val projectId: String,
+    @ColumnInfo(name = "name") val projectName: String,
+    @ColumnInfo(name = "group_code") val groupCode: String,
+    @ColumnInfo(name = "latitude") val latitude: String?,
+    @ColumnInfo(name = "longitude") val longitude: String?,
+    @ColumnInfo(name = "address") val detailAddress: String?,
+    @ColumnInfo(name = "local_id") val projectLocalID: String?,
+    @ColumnInfo(name = "local_name") val projectLocalName: String?,
+    @ColumnInfo(name = "function_type") val functionType: String?,
+    @ColumnInfo(name = "live_status") val liveStatus: String?,
+    @ColumnInfo(name = "user_id") val userId: String?
+)

+ 20 - 0
demo/src/main/java/com/framework/mvvm/model/db/entity/UserEntity.kt

@@ -0,0 +1,20 @@
+package com.framework.mvvm.model.db.entity
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "user")
+class UserEntity(
+    // @PrimaryKey(autoGenerate = true) var id: Int = 0,      // id
+    @PrimaryKey @ColumnInfo(name = "user_id") val userId: String,      // 用户ID
+    @ColumnInfo(name = "username") val username: String,   // 用户名
+    @ColumnInfo(name = "password") val password: String,   // 用户密码
+    @ColumnInfo(name = "pd") val pd: String,               //
+    @ColumnInfo(name = "device") val device: String,       // 登录设备
+    @ColumnInfo(name = "is_admin") val isAdmin: String?,   // 是否是管理员
+    @ColumnInfo(name = "mail") val mail: String?,          // 邮件
+    @ColumnInfo(name = "phone") val phone: String?,        // 电话
+    @ColumnInfo(name = "from") val from: String?,
+    @ColumnInfo(name = "state") var state: Int             // 登录状态 0 未登录, 1 登录
+)

+ 108 - 8
demo/src/main/java/com/framework/mvvm/model/repository/AdmRepository.kt

@@ -10,11 +10,16 @@ import com.framework.app.base.IRepository
 import com.framework.app.http.FileDownloadInfo
 import com.framework.app.http.FileInfo
 import com.framework.app.http.FileUploadInfo
+import com.framework.app.tools.md5
 import com.framework.mvvm.model.data.*
 import com.framework.mvvm.model.db.AdmDatabase
+import com.framework.mvvm.model.db.entity.ProjectEntity
+import com.framework.mvvm.model.db.entity.UserEntity
 import com.framework.mvvm.model.db.entity.dict.*
 import com.framework.mvvm.model.db.entity.task.*
 import com.framework.mvvm.model.vo.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
 import okhttp3.RequestBody
 
 class AdmRepository(
@@ -280,12 +285,8 @@ class AdmRepository(
         request(
             from = { api.getFrame(request) },
             to = { frame ->
-                val projects = frame.projects
                 val objects = frame.buildingsAndFloors
                 try {
-                    if (projects != null) {
-                        db.projectDao().insProject(projects)
-                    }
                     objects.forEach { it.state = 2 }.let { db.objectDao().insObjects(objects) }
                 } catch (e: Exception) {
                     Log.d("IRepository:${AdmRepository::class.simpleName}", "$e")
@@ -573,9 +574,108 @@ class AdmRepository(
     /**
      * 获取 Client
      */
-    suspend fun getClient(user: User) {
-        val model = api.getClient(user)
-        val client = model.data
-        sp.edit { putString(CLIENT_ID, client.clientId) }
+    suspend fun getClient(client: Client) {
+        val model = api.getClient(client)
+        sp.edit { putString(CLIENT_ID, model.data.clientId) }
+    }
+
+    /**
+     * 登录
+     */
+    suspend fun login(login: Login): List<Long> {
+        val json =
+            """{"loginDevice":"${login.device}","loginName":"${login.userName}","password":"${login.password}"}"""
+        var result = listOf<Long>()
+        withContext(Dispatchers.IO) {
+            request(
+                from = { api.login(json) },
+                to = {
+                    val one = it[0]
+                    val user = UserEntity(
+                        userId = one.userId,
+                        username = one.userName,
+                        password = login.password.md5(),
+                        pd = one.pd,
+                        device = one.loginDevice,
+                        isAdmin = one.isAdmin,
+                        mail = one.person_mail,
+                        phone = one.phone_num,
+                        from = one.userFrom,
+                        state = 1
+                    )
+
+                    val userDao = db.userDao()
+                    val users = userDao.getUsers().onEach { u -> u.state = 0 }
+                    users.add(user)
+                    result = userDao.insUsers(users)
+                },
+                error = {
+                    println("login error :$it")
+                }
+            )
+        }
+        return result
+    }
+
+    suspend fun getProjects(user: UserEntity) {
+        val json =
+            """{"puser":{"userId":"${user.userId}","loginDevice":"${user.device}","pd":"${user.pd}"}}"""
+        withContext(Dispatchers.IO) {
+            request(
+                from = { api.getProjects(json) },
+                to = { results ->
+                    println("results: $results")
+                    results.forEach {
+                        it.projects.forEach { p ->
+                            val project = ProjectEntity(
+                                projectId = p.projectId,
+                                projectName = p.projectName,
+                                groupCode = p.groupCode,
+                                latitude = p.latitude,
+                                longitude = p.longitude,
+                                detailAddress = p.detailAddress,
+                                projectLocalID = p.projectLocalID,
+                                projectLocalName = p.projectLocalName,
+                                functionType = p.functionType,
+                                liveStatus = p.liveStatus,
+                                userId = it.userId
+                            )
+                            val projectDao = db.projectDao()
+                            projectDao.insProject(project)
+                        }
+                    }
+                },
+                error = {
+                    println("getProjects error: $it")
+                }
+            )
+        }
     }
+
+    suspend fun getProjectsFromDB(): List<ProjectEntity> {
+        return withContext(Dispatchers.IO) {
+            db.projectDao().getProjects()
+        }
+    }
+
+    suspend fun getUser(username: String, password: String): UserEntity? {
+        return withContext(Dispatchers.IO) {
+            db.userDao().getUser(username, password.md5())
+        }
+    }
+
+    // 获取数据库用户
+    suspend fun getUsers(): List<UserEntity> {
+        return withContext(Dispatchers.IO) {
+            db.userDao().getUsers()
+        }
+    }
+
+    // 根据登录状态获取User
+    suspend fun getUserByState(state: Int): UserEntity {
+        return withContext(Dispatchers.IO) {
+            db.userDao().getUserByState(state)
+        }
+    }
+
 }

+ 1 - 1
demo/src/main/java/com/framework/mvvm/model/vo/Client.kt

@@ -3,4 +3,4 @@ package com.framework.mvvm.model.vo
 /**
  * 存储客户端信息
  */
-data class Client(val clientId: String)
+data class Client(val userId: String = "bdtp", val clientId: String = "")

+ 11 - 0
demo/src/main/java/com/framework/mvvm/model/vo/Login.kt

@@ -0,0 +1,11 @@
+package com.framework.mvvm.model.vo
+
+
+/**
+ * Login
+ */
+data class Login(
+    val userName: String = "",
+    val password: String = "",
+    val device: String = "Android"
+)

+ 17 - 2
demo/src/main/java/com/framework/mvvm/model/vo/User.kt

@@ -1,6 +1,21 @@
 package com.framework.mvvm.model.vo
 
+import com.framework.mvvm.model.db.entity.ProjectEntity
+
 /**
- * User
+ * Login Result
  */
-data class User(val userId: String = "bdtp")
+data class User(
+    val userId: String,
+    val userName: String,
+    val pd: String,
+    val groupCode: String,
+    val projects: List<ProjectEntity>,
+    // val authorizations: List<Any>,
+    val loginDevice: String,
+    val isAdmin: String,
+    val person_mail: String,
+    val phone_num: String,
+    val resultType: String,
+    val userFrom: String
+)

+ 37 - 6
demo/src/main/java/com/framework/mvvm/mv/AdmViewModel.kt

@@ -11,19 +11,22 @@ import com.framework.app.http.FileInfo
 import com.framework.app.http.FileUploadInfo
 import com.framework.app.tools.md5
 import com.framework.app.tools.toBytes
+import com.framework.mvvm.model.data.Data
 import com.framework.mvvm.model.data.Response
 import com.framework.mvvm.model.data.SuccessResponse
 import com.framework.mvvm.model.data.request
+import com.framework.mvvm.model.db.entity.ProjectEntity
+import com.framework.mvvm.model.db.entity.UserEntity
 import com.framework.mvvm.model.db.entity.task.ObjectEntity
 import com.framework.mvvm.model.repository.AdmRepository
 import com.framework.mvvm.model.vo.AdmRequest
-import com.framework.mvvm.model.vo.User
+import com.framework.mvvm.model.vo.Client
+import com.framework.mvvm.model.vo.Login
 import com.sybotan.android.demo.DemoApp
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import kotlinx.coroutines.flow.*
 import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import okhttp3.RequestBody
@@ -245,10 +248,38 @@ class AdmViewModel(private val repository: AdmRepository) : ViewModel() {
     /**
      * 获取服务端生成的客户端Id
      */
-    fun getClient(user: User) {
+    fun getClient(client: Client) {
         viewModelScope.launch {
-            repository.getClient(user)
+            repository.getClient(client)
         }
     }
 
+    /**
+     * 登录
+     */
+    suspend fun login(login: Login): List<Long> {
+        val def = viewModelScope.async {
+            repository.login(login)
+        }
+        return def.await()
+    }
+
+    suspend fun getUser(username: String, password: String): UserEntity? {
+        val def = viewModelScope.async {
+            repository.getUser(username, password)
+        }
+        return def.await()
+    }
+
+    suspend fun getUsers(): List<UserEntity> {
+        return repository.getUsers()
+    }
+
+    suspend fun getProjects(): List<ProjectEntity> {
+        val user = repository.getUserByState(1)
+        repository.getProjects(user)
+        return repository.getProjectsFromDB()
+
+    }
+
 }

+ 33 - 0
demo/src/main/java/com/framework/mvvm/ui/AdmApp.kt

@@ -0,0 +1,33 @@
+package com.framework.mvvm.ui
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.compositionLocalOf
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import com.framework.mvvm.ui.screen.FlashScreen
+import com.framework.mvvm.ui.screen.LoginScreen
+import com.framework.mvvm.ui.screen.ProjectScreen
+
+val LocalNavHostController = compositionLocalOf<NavHostController?> { null }
+
+@Composable
+fun AdmApp() {
+    val navController: NavHostController = rememberNavController()
+    CompositionLocalProvider(LocalNavHostController provides navController) {
+        NavHost(navController = navController, startDestination = "flash") {
+            composable("flash") {
+                FlashScreen()
+            }
+            composable("login") {
+                LoginScreen()
+            }
+            composable("project") {
+                ProjectScreen()
+            }
+        }
+    }
+
+}

+ 48 - 0
demo/src/main/java/com/framework/mvvm/ui/LoginActivity.kt

@@ -0,0 +1,48 @@
+package com.framework.mvvm.ui
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import org.kodein.di.DI
+import org.kodein.di.DIAware
+import org.kodein.di.android.closestDI
+
+class LoginActivity : ComponentActivity(), DIAware {
+    override val di: DI by closestDI()
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            MaterialTheme {
+                // A surface container using the 'background' color from the theme
+                Surface(
+                    modifier = Modifier.fillMaxSize(),
+                    color = MaterialTheme.colors.background
+                ) {
+                    AdmApp()
+                }
+            }
+        }
+    }
+
+}
+
+@Composable
+fun Greeting(name: String) {
+    Text(text = "Hello $name!")
+}
+
+@Preview(showBackground = true)
+@Composable
+fun DefaultPreview() {
+    MaterialTheme {
+        Greeting("Android")
+    }
+}

+ 82 - 0
demo/src/main/java/com/framework/mvvm/ui/screen/FlashScreen.kt

@@ -0,0 +1,82 @@
+package com.framework.mvvm.ui.screen
+
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.em
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.ConstraintSet
+import com.framework.mvvm.mv.AdmViewModel
+import com.framework.mvvm.ui.LocalNavHostController
+import kotlinx.coroutines.delay
+import org.kodein.di.compose.localDI
+import org.kodein.di.instance
+
+@Composable
+fun FlashScreen() {
+    val di = localDI()
+    val vm by di.instance<AdmViewModel>()
+    val nav = LocalNavHostController.current!!
+
+    BoxWithConstraints(
+        modifier = Modifier.fillMaxSize(),
+        contentAlignment = Alignment.Center
+    ) {
+
+        ConstraintLayout(getConstraints()) {
+            Text(text = "禹", Modifier.layoutId("text1"), fontSize = 24.em)
+            Text(text = "数", Modifier.layoutId("text2"), fontSize = 24.em)
+            Text(text = "ADM", Modifier.layoutId("text3"), fontSize = 12.em)
+        }
+
+        LaunchedEffect("flash") {
+            val users = vm.getUsers()
+            delay(1500)
+
+            if (users.isNotEmpty()) {
+                nav.navigate("project") {
+                    launchSingleTop = true
+                    popUpTo("flash") { inclusive = true }
+                }
+            } else {
+                nav.navigate("login") {
+                    launchSingleTop = true
+                    popUpTo("flash") { inclusive = true }
+                }
+            }
+        }
+    }
+}
+
+private fun getConstraints(): ConstraintSet {
+    return ConstraintSet {
+        val text1 = createRefFor("text1")
+        val text2 = createRefFor("text2")
+        val text3 = createRefFor("text3")
+
+        constrain(text1) {
+            top.linkTo(parent.top)
+            bottom.linkTo(text2.top, margin = 16.dp)
+            start.linkTo(parent.start)
+            end.linkTo(parent.end)
+        }
+        constrain(text2) {
+            top.linkTo(text1.bottom, margin = 16.dp)
+            bottom.linkTo(text3.top, margin = 16.dp)
+            start.linkTo(parent.start)
+            end.linkTo(parent.end)
+        }
+        constrain(text3) {
+            top.linkTo(text2.bottom, margin = 32.dp)
+            bottom.linkTo(parent.bottom)
+            start.linkTo(parent.start)
+            end.linkTo(parent.end)
+        }
+    }
+}

+ 132 - 0
demo/src/main/java/com/framework/mvvm/ui/screen/LoginScreen.kt

@@ -0,0 +1,132 @@
+package com.framework.mvvm.ui.screen
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.framework.mvvm.model.vo.Login
+import com.framework.mvvm.mv.AdmViewModel
+import com.framework.mvvm.ui.LocalNavHostController
+import com.sybotan.android.demo.R
+import kotlinx.coroutines.launch
+import org.kodein.di.compose.localDI
+import org.kodein.di.instance
+
+@Composable
+fun LoginScreen() {
+    val di = localDI()
+    val vm by di.instance<AdmViewModel>()
+    val scope = rememberCoroutineScope()
+    val nav = LocalNavHostController.current!!
+
+    var username by remember { mutableStateOf("") }
+    var password by remember { mutableStateOf("") }
+    var isOpenDialog by remember { mutableStateOf(false) }
+    var alert by remember { mutableStateOf("") }
+
+    Column(
+        modifier = Modifier
+            .fillMaxSize()
+            .background(Color.White),
+        verticalArrangement = Arrangement.Top,
+        horizontalAlignment = Alignment.CenterHorizontally
+    ) {
+        Spacer(modifier = Modifier.height(160.dp))
+
+        Image(
+            painter = painterResource(id = R.drawable.logo),
+            contentDescription = null,
+            modifier = Modifier
+                .width(160.dp)
+        )
+
+        Spacer(modifier = Modifier.height(72.dp))
+
+        OutlinedTextField(
+            value = username,
+            onValueChange = { text ->
+                username = text
+            },
+            label = { Text("用户名") }
+        )
+
+        Spacer(modifier = Modifier.height(16.dp))
+
+        OutlinedTextField(
+            value = password,
+            onValueChange = { text -> password = text },
+            label = { Text("密 码") }
+        )
+
+        Spacer(modifier = Modifier.height(48.dp))
+
+        Button(
+            modifier = Modifier
+                .width(160.dp)
+                .height(56.dp),
+            onClick = {
+                if (username.isEmpty()) {
+                    alert = "用户名不能为空"
+                    isOpenDialog = true
+                    return@Button
+                }
+
+                if (password.isEmpty()) {
+                    isOpenDialog = true
+                    alert = "密码不能为空"
+                    return@Button
+                }
+
+                scope.launch {
+                    val result = vm.login(Login(userName = username, password = password))
+                    if (result.isNotEmpty()) {
+                        nav.navigate("project") {
+                            popUpTo("login") { inclusive = true }
+                        }
+                    }
+                }
+
+            }) {
+            Text(text = "登录", fontSize = 18.sp)
+        }
+
+        if (isOpenDialog) {
+            AlertDialog(
+                onDismissRequest = {
+                    isOpenDialog = false
+                    alert = ""
+                },
+                title = {
+                    Text(text = "提示")
+                },
+                text = {
+                    Text(text = alert)
+                },
+                confirmButton = {
+                    TextButton(
+                        onClick = {
+                            isOpenDialog = false
+                            alert = ""
+                        }
+                    ) {
+                        Text("OK")
+                    }
+                }
+            )
+        }
+    }
+}
+
+@Preview
+@Composable
+fun PreviewLoginScreen() {
+    LoginScreen()
+}

+ 74 - 0
demo/src/main/java/com/framework/mvvm/ui/screen/ProjectScreen.kt

@@ -0,0 +1,74 @@
+package com.framework.mvvm.ui.screen
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material.Text
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.framework.mvvm.model.db.entity.ProjectEntity
+import com.framework.mvvm.mv.AdmViewModel
+import com.sybotan.android.demo.activities.PocActivity
+import org.kodein.di.compose.localDI
+import org.kodein.di.instance
+
+@Composable
+fun ProjectScreen() {
+    val di = localDI()
+    val vm by di.instance<AdmViewModel>()
+    val context = LocalContext.current
+
+    var projects by remember { mutableStateOf<List<ProjectEntity>>(emptyList()) }
+
+    Column(modifier = Modifier.fillMaxWidth()) {
+        Row(
+            modifier = Modifier
+                .height(56.dp)
+                .fillMaxWidth(),
+            horizontalArrangement = Arrangement.Center,
+            verticalAlignment = Alignment.CenterVertically
+        ) {
+            Text(
+                text = "项目",
+                fontSize = 22.sp,
+            )
+        }
+
+        LaunchedEffect(key1 = "project") {
+            projects = vm.getProjects()
+        }
+
+        LazyColumn {
+            items(items = projects) { project ->
+                Row(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .height(56.dp)
+                        .background(Color.Gray)
+                        .clickable {
+                            val intent = Intent(context, PocActivity::class.java)
+                            val bundle = Bundle()
+                            bundle.putString("userId", project.userId)
+                            bundle.putString("groupCode", project.groupCode)
+                            bundle.putString("projectId", project.projectId)
+                            intent.putExtra("project", bundle)
+                            context.startActivity(intent)
+                        },
+                    verticalAlignment = Alignment.CenterVertically
+                ) {
+                    Text(text = project.projectName, fontSize = 18.sp)
+                }
+            }
+        }
+
+    }
+}

+ 2 - 3
demo/src/main/java/com/sybotan/android/demo/DemoApp.kt

@@ -28,12 +28,11 @@ import android.app.Application
 import android.content.Context
 import android.content.SharedPreferences
 import android.util.Log
-import cn.hutool.core.util.IdUtil
 import com.arashivision.sdkcamera.InstaCameraSDK
 import com.arashivision.sdkmedia.InstaMediaSDK
 import com.framework.app.CLIENT_ID
 import com.framework.di.*
-import com.framework.mvvm.model.vo.User
+import com.framework.mvvm.model.vo.Client
 import com.framework.mvvm.mv.AdmViewModel
 import com.sybotan.android.demo.tools.AssetsUtil
 import org.kodein.di.DI
@@ -92,7 +91,7 @@ class DemoApp : Application(), DIAware {
 
         val clientId = sp.getString(CLIENT_ID, "") ?: ""
         if (clientId.isEmpty()) {
-            mVm.getClient(User())
+            mVm.getClient(Client())
         }
 
     } // Function onCreate()

+ 2 - 11
demo/src/main/java/com/sybotan/android/demo/activities/PocActivity.kt

@@ -69,7 +69,6 @@ class PocActivity : BaseActivity(), DIAware {
     //private val request =
     //    AdmRequest(userId = "bdtp", projectId = "Pj5001120003", groupCode = "XG")
 
-    @RequiresApi(Build.VERSION_CODES.R)
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_poc)
@@ -84,16 +83,8 @@ class PocActivity : BaseActivity(), DIAware {
 
         version = findViewById(R.id.version)
 
-        space3DBtn.setOnClickListener {
-            val intent = Intent(this, SpaceActivity::class.java)
-            intent.putExtra("is3D", true)
-            startActivity(intent)
-        }
-        scene3DBtn.setOnClickListener {
-            val intent = Intent(this, SceneActivity::class.java)
-            intent.putExtra("is3D", true)
-            startActivity(intent)
-        }
+        space3DBtn.setOnClickListener { startActivity<Space3DActivity>() }
+        scene3DBtn.setOnClickListener { startActivity<Scene3DActivity>() }
 
         downloadBtn.setOnClickListener {
             mVm.downloadBuilding(sp) {

+ 9 - 0
demo/src/main/java/com/sybotan/android/demo/ui/theme/Color.kt

@@ -0,0 +1,9 @@
+package com.sybotan.android.demo.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+
+val Purple200 = Color(0xFFBB86FC)
+val Purple500 = Color(0xFF6200EE)
+val Purple700 = Color(0xFF3700B3)
+val Teal200 = Color(0xFF03DAC5)

+ 11 - 0
demo/src/main/java/com/sybotan/android/demo/ui/theme/Shape.kt

@@ -0,0 +1,11 @@
+package com.sybotan.android.demo.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+    small = RoundedCornerShape(4.dp),
+    medium = RoundedCornerShape(4.dp),
+    large = RoundedCornerShape(0.dp)
+)

+ 45 - 0
demo/src/main/java/com/sybotan/android/demo/ui/theme/Theme.kt

@@ -0,0 +1,45 @@
+package com.sybotan.android.demo.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Shapes
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+    primary = Purple200,
+    primaryVariant = Purple700,
+    secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+    primary = Purple500,
+    primaryVariant = Purple700,
+    secondary = Teal200
+
+    /* Other default colors to override
+    background = Color.White,
+    surface = Color.White,
+    onPrimary = Color.White,
+    onSecondary = Color.Black,
+    onBackground = Color.Black,
+    onSurface = Color.Black,
+    */
+)
+
+@Composable
+fun AndroidTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
+    val colors = if (darkTheme) {
+        DarkColorPalette
+    } else {
+        LightColorPalette
+    }
+
+    MaterialTheme(
+        colors = colors,
+        typography = Typography,
+        shapes = Shapes,
+        content = content
+    )
+}

+ 34 - 0
demo/src/main/java/com/sybotan/android/demo/ui/theme/Type.kt

@@ -0,0 +1,34 @@
+package com.sybotan.android.demo.ui.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+    body1 = TextStyle(
+        fontFamily = FontFamily.Default,
+        fontWeight = FontWeight.Normal,
+        fontSize = 16.sp,
+        lineHeight = 24.sp,
+        letterSpacing = 0.5.sp
+    )
+    /* Other default text styles to override
+    titleLarge = TextStyle(
+        fontFamily = FontFamily.Default,
+        fontWeight = FontWeight.Normal,
+        fontSize = 22.sp,
+        lineHeight = 28.sp,
+        letterSpacing = 0.sp
+    ),
+    labelSmall = TextStyle(
+        fontFamily = FontFamily.Default,
+        fontWeight = FontWeight.Medium,
+        fontSize = 11.sp,
+        lineHeight = 16.sp,
+        letterSpacing = 0.5.sp
+    )
+    */
+)

BIN
demo/src/main/res/drawable/logo.png


+ 1 - 0
demo/src/main/res/values/strings.xml

@@ -39,4 +39,5 @@
     <string name="osc_dialog_msg_downloading">"总数 %1$d, 成功 %2$d, 失败 %3$d</string>
     <string name="toleft"><![CDATA[<-]]></string>
     <string name="toright"><![CDATA[->]]></string>
+    <string name="title_activity_login">LoginActivity</string>
 </resources>