xiongzhu 1 年間 前
コミット
e7172089a6

+ 15 - 6
app/build.gradle

@@ -2,7 +2,8 @@ plugins {
     alias(libs.plugins.androidApplication)
     alias(libs.plugins.jetbrainsKotlinAndroid)
     id 'kotlin-kapt'
-    id("com.google.dagger.hilt.android")
+    id 'com.google.dagger.hilt.android'
+    id 'org.jetbrains.kotlin.plugin.serialization'
 }
 
 android {
@@ -69,14 +70,14 @@ dependencies {
     implementation libs.navigation.fragment
     implementation libs.navigation.ui
     implementation libs.annotation
-    implementation(libs.datastore.preferences)
+    implementation libs.datastore.preferences
 
     implementation libs.core.ktx
     implementation libs.lifecycle.livedata.ktx
     implementation libs.lifecycle.viewmodel.ktx
     implementation libs.lifecycle.runtime.ktx
-    implementation libs.coroutines.core.ktx
-    implementation libs.coroutines.android.ktx
+    implementation libs.coroutines.core
+    implementation libs.coroutines.android
 
     implementation libs.gson
     implementation libs.commons.lang3
@@ -90,6 +91,14 @@ dependencies {
     implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01"
     implementation "androidx.work:work-runtime:2.9.0"
     implementation 'com.google.android.gms:play-services-code-scanner:16.1.0'
-    implementation("com.google.dagger:hilt-android:2.48")
-    kapt("com.google.dagger:hilt-android-compiler:2.48")
+    implementation libs.hilt.android
+    kapt libs.hilt.android.compiler
+
+    implementation libs.kotlin.stdlib.jdk8
+    implementation libs.ktor.client.core
+    implementation libs.ktor.client.cio
+    implementation libs.ktor.client.okhttp
+    implementation libs.ktor.client.content.negotiation
+    implementation libs.ktor.serialization.kotlinx.json
+    implementation libs.kotlinx.serialization.json
 }

+ 0 - 52
app/src/main/java/com/example/modifier/repository/ServerConfigRepository.kt

@@ -1,52 +0,0 @@
-package com.example.modifier.repository
-
-import android.content.Context
-import androidx.datastore.core.DataStore
-import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.core.edit
-import androidx.datastore.preferences.core.stringPreferencesKey
-import androidx.datastore.preferences.preferencesDataStore
-import com.example.modifier.model.ServerConfig
-import dagger.hilt.android.qualifiers.ApplicationContext
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.map
-import javax.inject.Inject
-
-val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "serverConfig")
-
-class ServerConfigRepository {
-
-    @Inject
-    @ApplicationContext
-    lateinit var context: Context
-
-    companion object {
-        val URL = stringPreferencesKey("url")
-        val USERNAME = stringPreferencesKey("username")
-        val PASSWORD = stringPreferencesKey("password")
-        val DEVICE_LABEL = stringPreferencesKey("device_label")
-        val TOKEN = stringPreferencesKey("token")
-    }
-
-    suspend fun saveServerConfig(
-        serverConfig: ServerConfig
-    ) {
-        context.dataStore.edit {
-            it[URL] = serverConfig.url ?: ""
-            it[USERNAME] = serverConfig.username ?: ""
-            it[PASSWORD] = serverConfig.password ?: ""
-            it[DEVICE_LABEL] = serverConfig.deviceLabel ?: ""
-            it[TOKEN] = serverConfig.token ?: ""
-        }
-    }
-
-    fun getServerConfig() = context.dataStore.data.map {
-        ServerConfig(
-            url = it[URL] ?: "",
-            username = it[USERNAME] ?: "",
-            password = it[PASSWORD] ?: "",
-            deviceLabel = it[DEVICE_LABEL] ?: "",
-            token = it[TOKEN] ?: ""
-        )
-    }
-}

+ 6 - 0
app/src/main/java/com/example/modifier/request/LoginRequest.kt

@@ -0,0 +1,6 @@
+package com.example.modifier.request
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class LoginRequest(val username: String, val password: String)

+ 6 - 0
app/src/main/java/com/example/modifier/response/ErrorResponse.kt

@@ -0,0 +1,6 @@
+package com.example.modifier.response
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class ErrorResponse(val statusCode: Int?, val message: String?)

+ 6 - 0
app/src/main/java/com/example/modifier/response/LoginResponse.kt

@@ -0,0 +1,6 @@
+package com.example.modifier.response
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class LoginResponse(val token: String?)

+ 14 - 5
app/src/main/java/com/example/modifier/ui/LoginActivity.kt

@@ -7,11 +7,19 @@ import androidx.appcompat.app.AppCompatActivity
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowInsetsCompat
 import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.viewModelScope
 import com.example.modifier.Global
 import com.example.modifier.R
+import com.example.modifier.Utils
 import com.example.modifier.databinding.ActivityLoginBinding
 import com.example.modifier.model.ServerConfig
 import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onSubscription
+import kotlinx.coroutines.flow.subscribe
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
 import org.apache.commons.lang3.StringUtils
 
 @AndroidEntryPoint
@@ -27,7 +35,6 @@ class LoginActivity : AppCompatActivity() {
 
         binding.etServer.setSimpleItems(Global.servers.toTypedArray())
         binding.btnLogin.setOnClickListener { v ->
-            loginViewModel.save()
             val server = binding.etDeviceLabel.text?.toString()
             val deviceLabel = binding.etDeviceLabel.text?.toString()
             val username = binding.etUsername.text?.toString()
@@ -49,12 +56,14 @@ class LoginActivity : AppCompatActivity() {
                 && StringUtils.isNotBlank(username)
                 && StringUtils.isNotBlank(password)
             ) {
-                Global.serverUrl = server
-                Global.name = deviceLabel
-                Global.load()
+                loginViewModel.viewModelScope.launch {
+                    loginViewModel.login()
+
+                }
+
             }
         }
-
         loginViewModel.serverConfig.postValue(ServerConfig())
+        loginViewModel.uiState.launchIn(loginViewModel.viewModelScope)
     }
 }

+ 5 - 2
app/src/main/java/com/example/modifier/ui/LoginUiState.kt

@@ -2,6 +2,9 @@ package com.example.modifier.ui
 
 data class LoginUiState(
     val isLoading: Boolean = false,
-    val errorMessage: String? = null,
-    val isUserLoggedIn: Boolean = false
+    val isUserLoggedIn: Boolean = false,
+    val serverError: String? = null,
+    val usernameError: String? = null,
+    val passwordError: String? = null,
+    val deviceLabelError: String? = null
 )

+ 81 - 11
app/src/main/java/com/example/modifier/ui/LoginViewModel.kt

@@ -3,24 +3,50 @@ package com.example.modifier.ui
 import android.content.Context
 import android.util.Log
 import androidx.datastore.core.DataStore
-import androidx.datastore.dataStore
 import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.core.intPreferencesKey
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
 import androidx.datastore.preferences.preferencesDataStore
-import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import com.example.modifier.model.ServerConfig
-import com.example.modifier.repository.ServerConfigRepository
+import com.example.modifier.request.LoginRequest
+import com.example.modifier.response.ErrorResponse
+import com.example.modifier.response.LoginResponse
 import dagger.hilt.android.lifecycle.HiltViewModel
 import dagger.hilt.android.qualifiers.ApplicationContext
+import io.ktor.client.HttpClient
+import io.ktor.client.call.body
+import io.ktor.client.engine.okhttp.OkHttp
+import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
+import io.ktor.client.plugins.defaultRequest
+import io.ktor.client.request.post
+import io.ktor.client.request.setBody
+import io.ktor.http.ContentType
+import io.ktor.http.HttpStatusCode
+import io.ktor.http.contentType
+import io.ktor.serialization.kotlinx.json.json
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.serialization.json.Json
 import javax.inject.Inject
 
+val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "serverConfig")
+
 @HiltViewModel
 class LoginViewModel @Inject constructor() : ViewModel() {
+    val TAG = "LoginViewModel"
+
+    companion object {
+        val URL = stringPreferencesKey("url")
+        val USERNAME = stringPreferencesKey("username")
+        val PASSWORD = stringPreferencesKey("password")
+        val DEVICE_LABEL = stringPreferencesKey("device_label")
+        val TOKEN = stringPreferencesKey("token")
+    }
+
     private val _uiState = MutableStateFlow(LoginUiState())
     val uiState: StateFlow<LoginUiState> = _uiState.asStateFlow()
     val serverConfig = MutableLiveData<ServerConfig>()
@@ -29,16 +55,60 @@ class LoginViewModel @Inject constructor() : ViewModel() {
     @ApplicationContext
     lateinit var appContext: Context
 
-    fun load() {
-        serverConfig.postValue(ServerConfig())
+    suspend fun loadConfig() {
+        appContext.dataStore.data.map {
+            ServerConfig(
+                url = it[URL] ?: "",
+                username = it[USERNAME] ?: "",
+                password = it[PASSWORD] ?: "",
+                deviceLabel = it[DEVICE_LABEL] ?: "",
+                token = it[TOKEN] ?: ""
+            )
+        }.collect {
+            serverConfig.postValue(it)
+        };
+    }
+
+    suspend fun saveConfig() {
+        appContext.dataStore.edit {
+            it[URL] = serverConfig.value?.url ?: ""
+            it[USERNAME] = serverConfig.value?.username ?: ""
+            it[PASSWORD] = serverConfig.value?.password ?: ""
+            it[DEVICE_LABEL] = serverConfig.value?.deviceLabel ?: ""
+            it[TOKEN] = serverConfig.value?.token ?: ""
+        }
     }
 
-    fun save() {
-        if (this::appContext.isInitialized) {
-            Log.d("LoginViewModel", "appContext is initialized")
-        } else {
-            Log.d("LoginViewModel", "appContext is not initialized")
+    suspend fun login() {
+        Log.d(TAG, "login")
+        _uiState.emit(LoginUiState(isLoading = true))
+        val client = HttpClient(OkHttp) {
+            defaultRequest {
+                url(serverConfig.value!!.url!!)
+            }
+            install(ContentNegotiation) {
+                json(Json {
+                    prettyPrint = true
+                    isLenient = true
+                    ignoreUnknownKeys = true
+                })
+            }
         }
 
+        val response = client.post("/api/auth/login") {
+            contentType(ContentType.Application.Json)
+            setBody(LoginRequest(serverConfig.value!!.username!!, serverConfig.value!!.password!!))
+        }
+        when (response.status) {
+            HttpStatusCode.OK -> {
+                val loginResponse = response.body<LoginResponse>()
+                Log.i(TAG, "response: $loginResponse")
+            }
+
+            else -> {
+                val errorResponse = response.body<ErrorResponse>()
+                Log.e(TAG, "error: $errorResponse")
+            }
+        }
     }
 }

+ 4 - 1
build.gradle

@@ -2,5 +2,8 @@
 plugins {
     alias(libs.plugins.androidApplication) apply false
     alias(libs.plugins.jetbrainsKotlinAndroid) apply false
-    id("com.google.dagger.hilt.android") version "2.48" apply false
+    alias(libs.plugins.hiltAndroid) apply false
+
+    alias(libs.plugins.kotlinSerialization) apply false
+
 }

+ 18 - 5
gradle/libs.versions.toml

@@ -5,18 +5,21 @@ commonsIo = "2.16.1"
 commonsLang3 = "3.14.0"
 datastore = "1.1.1"
 gson = "2.10.1"
+hilt = "2.48"
 junit = "4.13.2"
 junitVersion = "1.1.5"
 espressoCore = "3.5.1"
 appcompat = "1.6.1"
 kotlinxCoroutines = "1.8.1"
+kotlinxSerializationJson = "1.5.1"
+ktor = "2.3.11"
 material = "1.12.0"
 activity = "1.9.0"
 constraintlayout = "2.1.4"
 socketIoClient = "2.0.0"
 navigationFragment = "2.7.7"
 navigationUi = "2.7.7"
-kotlin = "1.9.0"
+kotlin = "1.9.24"
 annotation = "1.8.0"
 lifecycle = "2.8.0"
 coreKtx = "1.13.1"
@@ -27,12 +30,21 @@ commons-io = { module = "commons-io:commons-io", version.ref = "commonsIo" }
 commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commonsLang3" }
 datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
 gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
+hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
+hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
 junit = { group = "junit", name = "junit", version.ref = "junit" }
 ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
 espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
 appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
-coroutines-android-ktx = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
-coroutines-core-ktx = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" }
+coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
+coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" }
+kotlin-stdlib-jdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
+kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
+ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktor" }
+ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
+ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" }
+ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" }
+ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" }
 material = { group = "com.google.android.material", name = "material", version.ref = "material" }
 activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
 constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
@@ -47,5 +59,6 @@ core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx"
 
 [plugins]
 androidApplication = { id = "com.android.application", version.ref = "agp" }
-jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version = "1.9.24" }
-
+jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
+hiltAndroid = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }