From 9998234e46b4a4b48992c1be08f31c09eb5731da Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 14 Nov 2023 01:15:27 +0900 Subject: [PATCH 01/16] =?UTF-8?q?[feat]=EC=84=9C=EB=B2=84=20=ED=86=B5?= =?UTF-8?q?=EC=8B=A0login=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 16 ++++++++++++- app/src/main/AndroidManifest.xml | 5 ++-- .../sopt/dosopttemplate/data/APIFactory.kt | 24 +++++++++++++++++++ .../sopt/dosopttemplate/data/AuthService.kt | 12 ++++++++++ .../dosopttemplate/data/RequestLoginDto.kt | 12 ++++++++++ .../dosopttemplate/data/ResponseLoginDto.kt | 14 +++++++++++ 6 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/org/sopt/dosopttemplate/data/APIFactory.kt create mode 100644 app/src/main/java/org/sopt/dosopttemplate/data/AuthService.kt create mode 100644 app/src/main/java/org/sopt/dosopttemplate/data/RequestLoginDto.kt create mode 100644 app/src/main/java/org/sopt/dosopttemplate/data/ResponseLoginDto.kt diff --git a/app/build.gradle b/app/build.gradle index edafe73..c8b4673 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,8 +2,10 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' id 'kotlin-parcelize' + id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.20' } - +Properties properties = new Properties() +properties.load(project.rootProject.file('local.properties').newDataInputStream()) android { namespace 'org.sopt.dosopttemplate' compileSdk 33 @@ -16,10 +18,12 @@ android { versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + buildConfigField "String", "AUTH_BASE_URL", properties["base.url"] } buildFeatures { viewBinding true dataBinding true + buildConfig true } buildTypes { release { @@ -49,4 +53,14 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1' + implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0' + +// define a BOM and its version + implementation(platform("com.squareup.okhttp3:okhttp-bom:4.10.0")) + +// define any required OkHttp artifacts without version + implementation("com.squareup.okhttp3:okhttp") + implementation("com.squareup.okhttp3:logging-interceptor") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8be4069..83fffd5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + + tools:targetApi="31" + android:usesCleartextTraffic="true"> diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/APIFactory.kt b/app/src/main/java/org/sopt/dosopttemplate/data/APIFactory.kt new file mode 100644 index 0000000..de9cd96 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/data/APIFactory.kt @@ -0,0 +1,24 @@ +package org.sopt.dosopttemplate.data + +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import org.sopt.dosopttemplate.BuildConfig +import retrofit2.Retrofit + +object ApiFactory { + private const val BASE_URL = BuildConfig.AUTH_BASE_URL + + val retrofit: Retrofit by lazy{ + Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .build() + } + + inline fun create(): T = retrofit.create(T::class.java) +} + +object ServicePool { + val authService = ApiFactory.create() +} diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/AuthService.kt b/app/src/main/java/org/sopt/dosopttemplate/data/AuthService.kt new file mode 100644 index 0000000..fd2b34e --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/data/AuthService.kt @@ -0,0 +1,12 @@ +package org.sopt.dosopttemplate.data + +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.POST + +interface AuthService { + @POST("api/v1/members/sign-in") + fun login( + @Body request: RequestLoginDto, + ): Call +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/RequestLoginDto.kt b/app/src/main/java/org/sopt/dosopttemplate/data/RequestLoginDto.kt new file mode 100644 index 0000000..6b3bcf0 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/data/RequestLoginDto.kt @@ -0,0 +1,12 @@ +package org.sopt.dosopttemplate.data + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestLoginDto( + @SerialName("username") + val username: String, + @SerialName("password") + val password: String, +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/ResponseLoginDto.kt b/app/src/main/java/org/sopt/dosopttemplate/data/ResponseLoginDto.kt new file mode 100644 index 0000000..b69e2a6 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/data/ResponseLoginDto.kt @@ -0,0 +1,14 @@ +package org.sopt.dosopttemplate.data + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseLoginDto( + @SerialName("id") + val id: Int, + @SerialName("username") + val username: String, + @SerialName("nickname") + val nickname: String, +) From 15ed22d9721e8de441a856a7a602fceac2e1fb95 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 14 Nov 2023 12:51:49 +0900 Subject: [PATCH 02/16] =?UTF-8?q?[feat]=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=84=9C=EB=B2=84=20=ED=86=B5=EC=8B=A0=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 15 ++--- .../{data => API}/APIFactory.kt | 21 ++++++- .../{data => API}/AuthService.kt | 11 +++- .../{data => API}/RequestLoginDto.kt | 2 +- .../dosopttemplate/API/RequestSignUpDto.kt | 14 +++++ .../{data => API}/ResponseLoginDto.kt | 2 +- .../sopt/dosopttemplate/API/SignUpResponse.kt | 7 +++ .../presentation/login/LogInViewModel.kt | 5 -- .../presentation/login/LoginActivity.kt | 60 +++++++++++++------ .../presentation/signup/SignUpActivity.kt | 38 +++++++++++- .../presentation/signup/SignUpViewModel.kt | 2 + .../org/sopt/dosopttemplate/util/IntentExt.kt | 9 --- .../sopt/dosopttemplate/util/ToastMaker.kt | 5 +- .../{activity_main.xml => activity_login.xml} | 0 app/src/main/res/values/strings.xml | 6 ++ 15 files changed, 149 insertions(+), 48 deletions(-) rename app/src/main/java/org/sopt/dosopttemplate/{data => API}/APIFactory.kt (50%) rename app/src/main/java/org/sopt/dosopttemplate/{data => API}/AuthService.kt (60%) rename app/src/main/java/org/sopt/dosopttemplate/{data => API}/RequestLoginDto.kt (86%) create mode 100644 app/src/main/java/org/sopt/dosopttemplate/API/RequestSignUpDto.kt rename app/src/main/java/org/sopt/dosopttemplate/{data => API}/ResponseLoginDto.kt (88%) create mode 100644 app/src/main/java/org/sopt/dosopttemplate/API/SignUpResponse.kt rename app/src/main/res/layout/{activity_main.xml => activity_login.xml} (100%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 83fffd5..2d129e8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,19 +15,20 @@ android:usesCleartextTraffic="true"> + android:exported="true"> + + + + + android:exported="false"> + - - - - + android:exported="false"> + Log.d("Retrofit2", "CONNECTION INFO -> $message") + } + loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY + return loggingInterceptor + } + + val okHttpClient = OkHttpClient.Builder() + .addInterceptor(getLogOkHttpClient()) + .build() + + val retrofit: Retrofit by lazy { Retrofit.Builder() .baseUrl(BASE_URL) + .client(okHttpClient) .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) .build() } diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/AuthService.kt b/app/src/main/java/org/sopt/dosopttemplate/API/AuthService.kt similarity index 60% rename from app/src/main/java/org/sopt/dosopttemplate/data/AuthService.kt rename to app/src/main/java/org/sopt/dosopttemplate/API/AuthService.kt index fd2b34e..bdb3b5c 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/data/AuthService.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/API/AuthService.kt @@ -1,4 +1,4 @@ -package org.sopt.dosopttemplate.data +package org.sopt.dosopttemplate.API import retrofit2.Call import retrofit2.http.Body @@ -9,4 +9,11 @@ interface AuthService { fun login( @Body request: RequestLoginDto, ): Call -} \ No newline at end of file + + @POST("api/v1/members") + fun signUp( + @Body request: RequestSignUpDto, + ): Call +} + + diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/RequestLoginDto.kt b/app/src/main/java/org/sopt/dosopttemplate/API/RequestLoginDto.kt similarity index 86% rename from app/src/main/java/org/sopt/dosopttemplate/data/RequestLoginDto.kt rename to app/src/main/java/org/sopt/dosopttemplate/API/RequestLoginDto.kt index 6b3bcf0..da17e2c 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/data/RequestLoginDto.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/API/RequestLoginDto.kt @@ -1,4 +1,4 @@ -package org.sopt.dosopttemplate.data +package org.sopt.dosopttemplate.API import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/RequestSignUpDto.kt b/app/src/main/java/org/sopt/dosopttemplate/API/RequestSignUpDto.kt new file mode 100644 index 0000000..63c2a32 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/API/RequestSignUpDto.kt @@ -0,0 +1,14 @@ +package org.sopt.dosopttemplate.API + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestSignUpDto( + @SerialName("username") + val username: String, + @SerialName("nickname") + val nickname: String, + @SerialName("password") + val password: String, +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/ResponseLoginDto.kt b/app/src/main/java/org/sopt/dosopttemplate/API/ResponseLoginDto.kt similarity index 88% rename from app/src/main/java/org/sopt/dosopttemplate/data/ResponseLoginDto.kt rename to app/src/main/java/org/sopt/dosopttemplate/API/ResponseLoginDto.kt index b69e2a6..be752f3 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/data/ResponseLoginDto.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/API/ResponseLoginDto.kt @@ -1,4 +1,4 @@ -package org.sopt.dosopttemplate.data +package org.sopt.dosopttemplate.API import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/SignUpResponse.kt b/app/src/main/java/org/sopt/dosopttemplate/API/SignUpResponse.kt new file mode 100644 index 0000000..bb6f810 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/API/SignUpResponse.kt @@ -0,0 +1,7 @@ +package org.sopt.dosopttemplate.API + +sealed class SignupResponse { + data class ResponseSignUpDto( + val location: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LogInViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LogInViewModel.kt index ac42d6a..3c71748 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LogInViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LogInViewModel.kt @@ -2,16 +2,11 @@ package org.sopt.dosopttemplate.presentation.login import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import org.sopt.dosopttemplate.data.SignUpInfo class LogInViewModel : ViewModel() { val id: MutableLiveData = MutableLiveData() val password: MutableLiveData = MutableLiveData() - fun isLoginAuthorized(signUpInfo: SignUpInfo): Boolean = - signUpInfo.password == password.value && signUpInfo.id == id.value - - companion object { const val SIGNUPINFO = "sign up info" } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt index 1c37a83..5963bdd 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt @@ -7,26 +7,31 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil +import org.sopt.dosopttemplate.API.RequestLoginDto +import org.sopt.dosopttemplate.API.ResponseLoginDto +import org.sopt.dosopttemplate.API.ServicePool.authService import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.data.SignUpInfo -import org.sopt.dosopttemplate.databinding.ActivityMainBinding +import org.sopt.dosopttemplate.databinding.ActivityLoginBinding import org.sopt.dosopttemplate.presentation.home.HomeActivity import org.sopt.dosopttemplate.presentation.signup.SingUpActivity import org.sopt.dosopttemplate.util.ToastMaker.makeToast import org.sopt.dosopttemplate.util.getParcelable +import retrofit2.Call +import retrofit2.Response class LoginActivity : AppCompatActivity() { - private lateinit var binding: ActivityMainBinding + private lateinit var binding: ActivityLoginBinding private lateinit var signUpInfo: SignUpInfo private val viewModel by viewModels() private lateinit var resultLauncher: ActivityResultLauncher override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = DataBindingUtil.setContentView(this, R.layout.activity_main) + binding = DataBindingUtil.setContentView(this, R.layout.activity_login) binding.lifecycleOwner = this binding.viewModel = viewModel getSignUpInfo() - clickLoginBtn() + login() clickSignUpBtn() } @@ -38,27 +43,48 @@ class LoginActivity : AppCompatActivity() { signUpInfo = result.data?.getParcelable(LogInViewModel.SIGNUPINFO, SignUpInfo::class.java) ?: return@registerForActivityResult - } } } - private fun clickLoginBtn() { - binding.btLogin.setOnClickListener() { -// Log.v("signUp Info", signUpInfo.id) - if (viewModel.isLoginAuthorized(signUpInfo)) { - makeToast(this, "로그인 성공!") - goToMainPage() - } else - makeToast(this, "ID 또는 비밀번호가 일치하지 않습니다.") + private fun login() { + val id = binding.etId.text.toString() + val password = binding.etPassword.text.toString() + + binding.btLogin.setOnClickListener { + authService.login(RequestLoginDto(id, password)) + .enqueue(object : retrofit2.Callback { + override fun onResponse( + call: Call, + response: Response, + ) { + if (response.isSuccessful) { + processLogin(response) + } + } + + override fun onFailure(call: Call, t: Throwable) { + makeToast(this@LoginActivity, getString(R.string.serverError)) + } + + }) + } + + } + + private fun processLogin(response: Response) { + val data: ResponseLoginDto = response.body()!! + val userId = data.id + makeToast( + this@LoginActivity, + getString(R.string.loginSuccess) + userId + getString(R.string.end) + ) + goToMainPage() } private fun goToMainPage() { - val intent = Intent(this, HomeActivity::class.java) - with(intent) { - putExtra("id", signUpInfo.id) - } + val intent = Intent(this@LoginActivity, HomeActivity::class.java) startActivity(intent) } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt index 3ceb130..bcd1b49 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt @@ -1,13 +1,17 @@ package org.sopt.dosopttemplate.presentation.signup import android.os.Bundle -import android.util.Log import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil +import org.sopt.dosopttemplate.API.RequestSignUpDto +import org.sopt.dosopttemplate.API.ServicePool.authService import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.databinding.ActivitySignupBinding import org.sopt.dosopttemplate.util.ToastMaker.makeToast +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response class SingUpActivity : AppCompatActivity() { private lateinit var binding: ActivitySignupBinding @@ -19,12 +23,13 @@ class SingUpActivity : AppCompatActivity() { binding.lifecycleOwner = this binding.viewModel = viewModel clickSignUpBtn() + signUp() } private fun clickSignUpBtn() { binding.btSignUp.setOnClickListener() { if (viewModel.isConditionSatisfied()) - signUp() + processSignUp() else { val errorString = "please check for " + viewModel.getInvalidFormatField() makeToast(this, errorString) @@ -33,6 +38,34 @@ class SingUpActivity : AppCompatActivity() { } private fun signUp() { + binding.btSignUp.setOnClickListener() { + val id = binding.etId.text.toString() + val password = binding.etPw.text.toString() + val nickname = binding.etNickName.text.toString() + + authService.signUp(RequestSignUpDto(id, nickname, password)) + .enqueue(object : Callback { + override fun onResponse( + call: Call, + response: Response + ) { + if (response.isSuccessful) { + processSignUp() + } else { + makeToast(this@SingUpActivity, getString(R.string.signUpFail)) + } + } + + override fun onFailure(call: Call, t: Throwable) { + makeToast(this@SingUpActivity, getString(R.string.serverError)) + } + + }) + + } + } + + private fun processSignUp() { makeToast(this, "회원가입 완료!") sendDataToLoginActivity() // TODO: saveSignUpData() @@ -53,4 +86,3 @@ class SingUpActivity : AppCompatActivity() { } - diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpViewModel.kt index 649e63b..34f3957 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpViewModel.kt @@ -14,11 +14,13 @@ class SignUpViewModel : ViewModel() { val nickName: MutableLiveData = MutableLiveData() val mbti: MutableLiveData = MutableLiveData() val intro: MutableLiveData = MutableLiveData() + //todo make music, artist validation val music: MutableLiveData = MutableLiveData() val artist: MutableLiveData = MutableLiveData() private final val MAX = 20 + fun isConditionSatisfied(): Boolean { return isIDFormatValid() && isPasswordFormatValid() diff --git a/app/src/main/java/org/sopt/dosopttemplate/util/IntentExt.kt b/app/src/main/java/org/sopt/dosopttemplate/util/IntentExt.kt index 36e53dd..8311804 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/util/IntentExt.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/util/IntentExt.kt @@ -11,13 +11,4 @@ fun Intent.getParcelable(name: String, clazz: Class): T? { } else getParcelableExtra(name) -} - -fun logProfile(profile: Profile) { - Log.v("nick name", profile.name.toString()) - Log.v("mbti", profile.MBTI.toString()) - Log.v("music title", profile.getMusic()) - Log.v("intro", profile.intro.toString()) - - } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/util/ToastMaker.kt b/app/src/main/java/org/sopt/dosopttemplate/util/ToastMaker.kt index 84e06bf..de705ce 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/util/ToastMaker.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/util/ToastMaker.kt @@ -2,6 +2,7 @@ package org.sopt.dosopttemplate.util import android.content.Context import android.widget.Toast +import androidx.lifecycle.MutableLiveData object ToastMaker { private var toast: Toast? = null @@ -10,4 +11,6 @@ object ToastMaker { toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT) toast?.show() } -} \ No newline at end of file +} + +fun getMutableDataString(str: MutableLiveData):String = str.value.toString() \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_login.xml similarity index 100% rename from app/src/main/res/layout/activity_main.xml rename to app/src/main/res/layout/activity_login.xml diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6a7a534..d27124e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,4 +33,10 @@ Edit My Page 완료 + 로그인에 성공하였고 + 입니다. + 서버 에러 발생 + + 회원가입 실패. + \ No newline at end of file From 7ca105e675e99ab2198c93fa896e548d857e9513 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Wed, 15 Nov 2023 01:13:21 +0900 Subject: [PATCH 03/16] =?UTF-8?q?[feat]=20reqres=20=EC=84=9C=EB=B2=84?= =?UTF-8?q?=ED=86=B5=EC=8B=A0=20=EA=B5=AC=ED=98=84,=20follower=20list=20?= =?UTF-8?q?=EB=A7=8C=EB=93=A4=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 6 ++ app/src/main/AndroidManifest.xml | 12 ++-- .../org/sopt/dosopttemplate/API/APIFactory.kt | 18 +++-- .../dosopttemplate/API/FollowerService.kt | 10 +++ .../API/ResponseGetFollwerDto.kt | 42 +++++++++++ .../org/sopt/dosopttemplate/data/Profile.kt | 65 ++++++++++++++++-- .../doandroid/DOAndroidFragment.kt | 47 +++++++++++++ .../doandroid/DOAndroidViewModel.kt | 16 +++++ .../presentation/home/FriendViewHolder.kt | 9 ++- .../presentation/home/MyProfileViewHolder.kt | 3 +- app/src/main/res/drawable/img_noritake.jpg | Bin 37658 -> 0 bytes .../main/res/layout/fragment_doandroid.xml | 14 ++-- app/src/main/res/values/strings.xml | 2 +- 13 files changed, 218 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/org/sopt/dosopttemplate/API/FollowerService.kt create mode 100644 app/src/main/java/org/sopt/dosopttemplate/API/ResponseGetFollwerDto.kt delete mode 100644 app/src/main/res/drawable/img_noritake.jpg diff --git a/app/build.gradle b/app/build.gradle index c8b4673..78687bb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,7 +18,10 @@ android { versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + buildConfigField "String", "REQRES_BASE_URL", properties["reqres.base.url"] buildConfigField "String", "AUTH_BASE_URL", properties["base.url"] + + } buildFeatures { viewBinding true @@ -63,4 +66,7 @@ dependencies { // define any required OkHttp artifacts without version implementation("com.squareup.okhttp3:okhttp") implementation("com.squareup.okhttp3:logging-interceptor") + + implementation 'com.github.bumptech.glide:glide:4.12.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2d129e8..3fb512b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,12 +15,8 @@ android:usesCleartextTraffic="true"> + android:exported="false"> - - - - + android:exported="true"> + + + + Log.d("Retrofit2", "CONNECTION INFO -> $message") @@ -27,15 +26,24 @@ object ApiFactory { val retrofit: Retrofit by lazy { Retrofit.Builder() - .baseUrl(BASE_URL) + .baseUrl(url) .client(okHttpClient) .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) .build() } - inline fun create(): T = retrofit.create(T::class.java) + inline fun create(url: String): T { + this.url = url + return retrofit.create(T::class.java) + } } object ServicePool { - val authService = ApiFactory.create() + private const val BASE_URL = BuildConfig.AUTH_BASE_URL + private const val REQRES_BASE_URL = BuildConfig.REQRES_BASE_URL + + val authService = ApiFactory.create(BASE_URL) + val followerService = ApiFactory.create(REQRES_BASE_URL) } + + diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/FollowerService.kt b/app/src/main/java/org/sopt/dosopttemplate/API/FollowerService.kt new file mode 100644 index 0000000..48090c8 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/API/FollowerService.kt @@ -0,0 +1,10 @@ +package org.sopt.dosopttemplate.API + +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.GET + +interface FollowerService { + @GET("https://reqres.in/api/users?page=2") + fun getFollowerList(): Call +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/ResponseGetFollwerDto.kt b/app/src/main/java/org/sopt/dosopttemplate/API/ResponseGetFollwerDto.kt new file mode 100644 index 0000000..b2fdb05 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/API/ResponseGetFollwerDto.kt @@ -0,0 +1,42 @@ +package org.sopt.dosopttemplate.API + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseData( + @SerialName("id") + val id: Int, + @SerialName("email") + val email: String, + @SerialName("first_name") + val first_name: String, + @SerialName("last_name") + val last_name: String, + @SerialName("avatar") + val avatar: String, +) + +@Serializable +data class ResponseSupport( + @SerialName("url") + val url: String, + @SerialName("text") + val text: String +) + +@Serializable +data class ResponseGetFollwerDto( + @SerialName("page") + val page: Int, + @SerialName("per_page") + val per_page: Int, + @SerialName("total") + val total: Int, + @SerialName("total_pages") + val total_pages: Int, + @SerialName("data") + val data: List, + @SerialName("support") + val support: ResponseSupport +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt b/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt index 1a79e63..107cd96 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt @@ -8,21 +8,24 @@ import org.sopt.dosopttemplate.R @Parcelize data class Profile( @DrawableRes - val profileImage: Int, + val profileImage: Int?, var name: String, var musicTitle: String?, var musicArtist: String?, val type: Int, var MBTI: String?, var intro: String?, - val id: String? + val id: String?, + val email: String?, + val avatar: String?, + val idInt: Int? ) : Parcelable { var music:Music? = null init { setMusic() } constructor( - @DrawableRes profileImage: Int, + profileImage: Int, name: String, musicTitle: String, musicArtist: String, @@ -35,6 +38,9 @@ data class Profile( type, null, null, + null, + null, + null, null ) @@ -44,13 +50,16 @@ data class Profile( MBTI: String, intro: String ) : this( - R.drawable.img_noritake, + R.drawable.img_monkey, name, null, null, type, MBTI, intro, + null, + null, + null, null ) @@ -69,9 +78,53 @@ data class Profile( type, MBTI, null, + null, + null, + null, + null + ) + + constructor( + profileImage: Int, + name: String, + musicTitle: String?, + musicArtist: String?, + type: Int, + MBTI: String?, + intro: String?, + id: String? + ) : this( + profileImage, + name, + musicTitle, + musicArtist, + type, + MBTI, + intro, + id, + null, + null, null ) + constructor( + name: String, + email: String, + avatar: String, + idInt: Int + ): this( + null, + name, + null, + null, + FRIEND, + null, + null, + null, + email, + avatar, + idInt + ) fun isContainMusic(): Boolean = (music != null) fun getMusic(): String = if (isContainMusic()) music!!.string else "no music" @@ -81,4 +134,8 @@ data class Profile( else this.music = null } + companion object { + const val ME = 1 + const val FRIEND = 0 + } } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidFragment.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidFragment.kt index 144ebbe..a8e735c 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidFragment.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidFragment.kt @@ -1,14 +1,61 @@ package org.sopt.dosopttemplate.presentation.doandroid +import android.app.Activity +import android.content.Context import android.os.Bundle +import android.util.Log import android.view.View +import androidx.fragment.app.viewModels +import org.sopt.dosopttemplate.API.ResponseGetFollwerDto +import org.sopt.dosopttemplate.API.ServicePool.followerService import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.databinding.FragmentDoandroidBinding +import org.sopt.dosopttemplate.presentation.home.ProfileAdapter +import org.sopt.dosopttemplate.util.ToastMaker.makeToast import org.sopt.dosopttemplate.util.binding.BindingFragment +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response class DOAndroidFragment : BindingFragment(R.layout.fragment_doandroid) { + val viewModel by viewModels() + private var _profileAdapter: ProfileAdapter? = null + val profileAdapter: ProfileAdapter + get() = requireNotNull(_profileAdapter) { "profileAdapter not initialized" } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + _profileAdapter = ProfileAdapter(requireContext()) + binding.rvProfiles.adapter = profileAdapter + getFollowerList() + } + + private fun getFollowerList() { + followerService.getFollowerList() + .enqueue(object : Callback { + override fun onResponse( + call: Call, + response: Response + ) { + if (response.isSuccessful) { + setProfileListFromResponse(response) + } + } + + override fun onFailure(call: Call, t: Throwable) { + Log.v("serverError", t.toString()) + val context = context as Activity + makeToast(context, getString(R.string.serverError)) + } + }) + } + + private fun setProfileListFromResponse(response: Response) { + val responseBody = response.body() + val data = responseBody?.data + Log.v("data", data.toString()) + viewModel.initResponseDataList(data) + profileAdapter.setProfileList(viewModel.getResponseDataList()) } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidViewModel.kt index cd40e85..3cb6408 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidViewModel.kt @@ -1,6 +1,22 @@ package org.sopt.dosopttemplate.presentation.doandroid import androidx.lifecycle.ViewModel +import org.sopt.dosopttemplate.API.ResponseData +import org.sopt.dosopttemplate.data.Profile class DOAndroidViewModel : ViewModel() { + private var _responseDataList: MutableList = mutableListOf() + + fun getResponseDataList(): MutableList = _responseDataList + fun initResponseDataList(responseDataList: List?) { + if (responseDataList.isNullOrEmpty()) return + for (data in responseDataList) { + _responseDataList.add(convertDataToProfile(data)) + } + } + + private fun convertDataToProfile(data: ResponseData): Profile = + Profile(getName(data), data.email, data.avatar, data.id) + + private fun getName(data: ResponseData): String = data.first_name + " " + data.last_name } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/FriendViewHolder.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/FriendViewHolder.kt index 8c712e4..c470248 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/FriendViewHolder.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/FriendViewHolder.kt @@ -1,13 +1,20 @@ package org.sopt.dosopttemplate.presentation.home import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide import org.sopt.dosopttemplate.data.Profile import org.sopt.dosopttemplate.databinding.ItemFriendBinding class FriendViewHolder(private val binding: ItemFriendBinding) : RecyclerView.ViewHolder(binding.root) { fun onBind(friendData: Profile) = with(binding) { - ivProfile.setImageResource(friendData.profileImage) + if (friendData.profileImage != null) + ivProfile.setImageResource(friendData.profileImage) + else { + Glide.with(binding.root) + .load(friendData.avatar) + .into(ivProfile) + } tvName.text = friendData.name tvMusic.text = friendData.music?.string } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/MyProfileViewHolder.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/MyProfileViewHolder.kt index ffbba2d..0206111 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/MyProfileViewHolder.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/MyProfileViewHolder.kt @@ -7,7 +7,8 @@ import org.sopt.dosopttemplate.databinding.ItemMyprofileBinding class MyProfileViewHolder(private val binding: ItemMyprofileBinding) : RecyclerView.ViewHolder(binding.root) { fun onBind(myProfileData: Profile) { - binding.ivProfile.setImageResource(myProfileData.profileImage) + if (myProfileData.profileImage != null) + binding.ivProfile.setImageResource(myProfileData.profileImage) binding.tvName.text = myProfileData.name } } \ No newline at end of file diff --git a/app/src/main/res/drawable/img_noritake.jpg b/app/src/main/res/drawable/img_noritake.jpg deleted file mode 100644 index e403b7d7cd8f5021d79e1bbfc4f8add7fe6a6c5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37658 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 - + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d27124e..b86850d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,7 +33,7 @@ Edit My Page 완료 - 로그인에 성공하였고 + 로그인에 성공하였고 유저 아이디는 입니다. 서버 에러 발생 From 108913bbf929c9940312cee0d37f56979357f1c1 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Thu, 16 Nov 2023 14:14:32 +0900 Subject: [PATCH 04/16] [refact] profile data class refactoring --- app/src/main/AndroidManifest.xml | 12 +- .../org/sopt/dosopttemplate/data/Music.kt | 26 +-- .../org/sopt/dosopttemplate/data/Profile.kt | 150 +++++++----------- .../doandroid/DOAndroidFragment.kt | 43 ----- .../doandroid/DOAndroidViewModel.kt | 16 -- .../editMyPage/EditMyPageViewModel.kt | 12 +- .../presentation/home/FriendViewHolder.kt | 6 +- .../presentation/home/HomeFragment.kt | 40 ++++- .../presentation/home/HomeViewModel.kt | 106 ++++--------- .../presentation/home/MyProfileViewHolder.kt | 2 +- .../presentation/login/LoginActivity.kt | 3 - .../presentation/myPage/MyPageViewModel.kt | 6 +- 12 files changed, 146 insertions(+), 276 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3fb512b..2d129e8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,8 +15,12 @@ android:usesCleartextTraffic="true"> + android:exported="true"> + + + + - - - - + android:exported="false"> { - override fun createFromParcel(parcel: Parcel): Music { - return Music(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt b/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt index 107cd96..1b076fa 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt @@ -5,135 +5,105 @@ import androidx.annotation.DrawableRes import kotlinx.parcelize.Parcelize import org.sopt.dosopttemplate.R +@Parcelize +data class ReqresData( + val email: String, + val avatar: String, + val idInt: Int +) : Parcelable + + +@Parcelize +data class subInfo( + var music: Music?, + var MBTI: String, + var intro: String, +) : Parcelable { + fun getMusic(): String = music?.string ?: "no music" + + constructor(music: Music?) : this(music, "", "") +} + @Parcelize data class Profile( @DrawableRes val profileImage: Int?, - var name: String, - var musicTitle: String?, - var musicArtist: String?, - val type: Int, - var MBTI: String?, - var intro: String?, val id: String?, - val email: String?, - val avatar: String?, - val idInt: Int? + var nickname: String, +// var music: Music?, + val type: Int, +// var MBTI: String?, +// var intro: String?, + val reqresData: ReqresData?, + val subInfo: subInfo ) : Parcelable { - var music:Music? = null - init { - setMusic() + fun getMusic(): String = subInfo.getMusic() + fun setMusic(musicTitle: String, musicArtist: String) { + subInfo.music = Music(musicTitle, musicArtist) + } + + fun getMbti(): String = subInfo.MBTI + fun setMbti(mbti: String) { + subInfo.MBTI = mbti + } + + fun getIntro(): String = subInfo.intro + fun setIntro(intro: String) { + subInfo.intro = intro } - constructor( - profileImage: Int, - name: String, - musicTitle: String, - musicArtist: String, - type: Int, - ) : this( - profileImage, - name, - musicTitle, - musicArtist, - type, - null, - null, - null, - null, - null, - null - ) constructor( - name: String, + nickname: String, type: Int, - MBTI: String, + mbti: String, intro: String ) : this( R.drawable.img_monkey, - name, - null, null, + nickname, +// null, type, - MBTI, - intro, - null, null, - null, - null + subInfo(null, mbti, intro) ) constructor( profileImage: Int, - name: String, + id : String, + nickname: String, musicTitle: String, musicArtist: String, type: Int, - MBTI: String, - ) : this( - profileImage, - name, - musicTitle, - musicArtist, - type, - MBTI, - null, - null, - null, - null, - null - ) - - constructor( - profileImage: Int, - name: String, - musicTitle: String?, - musicArtist: String?, - type: Int, - MBTI: String?, - intro: String?, - id: String? + mbti: String, + intro: String, ) : this( profileImage, - name, - musicTitle, - musicArtist, - type, - MBTI, - intro, id, + nickname, +// Music(musicTitle, musicArtist), + type, +// MBTI, +// intro, null, - null, - null + subInfo(Music(musicTitle, musicArtist), mbti, intro) ) constructor( - name: String, + nickname: String, email: String, avatar: String, idInt: Int - ): this( - null, - name, + ) : this( null, null, + nickname, +// null, FRIEND, - null, - null, - null, - email, - avatar, - idInt + ReqresData(email, avatar, idInt), + subInfo(null) ) - fun isContainMusic(): Boolean = (music != null) - fun getMusic(): String = if (isContainMusic()) music!!.string else "no music" - fun setMusic() { - if (musicArtist != null && musicTitle != null) - this.music = Music(musicTitle!!, musicArtist!!) - else this.music = null - } companion object { const val ME = 1 const val FRIEND = 0 diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidFragment.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidFragment.kt index a8e735c..e7ee7de 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidFragment.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidFragment.kt @@ -1,61 +1,18 @@ package org.sopt.dosopttemplate.presentation.doandroid -import android.app.Activity -import android.content.Context import android.os.Bundle -import android.util.Log import android.view.View import androidx.fragment.app.viewModels -import org.sopt.dosopttemplate.API.ResponseGetFollwerDto -import org.sopt.dosopttemplate.API.ServicePool.followerService import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.databinding.FragmentDoandroidBinding -import org.sopt.dosopttemplate.presentation.home.ProfileAdapter -import org.sopt.dosopttemplate.util.ToastMaker.makeToast import org.sopt.dosopttemplate.util.binding.BindingFragment -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response class DOAndroidFragment : BindingFragment(R.layout.fragment_doandroid) { val viewModel by viewModels() - private var _profileAdapter: ProfileAdapter? = null - val profileAdapter: ProfileAdapter - get() = requireNotNull(_profileAdapter) { "profileAdapter not initialized" } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - _profileAdapter = ProfileAdapter(requireContext()) - binding.rvProfiles.adapter = profileAdapter - getFollowerList() } - private fun getFollowerList() { - followerService.getFollowerList() - .enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - setProfileListFromResponse(response) - } - } - - override fun onFailure(call: Call, t: Throwable) { - Log.v("serverError", t.toString()) - val context = context as Activity - makeToast(context, getString(R.string.serverError)) - } - }) - } - - private fun setProfileListFromResponse(response: Response) { - val responseBody = response.body() - val data = responseBody?.data - Log.v("data", data.toString()) - viewModel.initResponseDataList(data) - profileAdapter.setProfileList(viewModel.getResponseDataList()) - } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidViewModel.kt index 3cb6408..cd40e85 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/doandroid/DOAndroidViewModel.kt @@ -1,22 +1,6 @@ package org.sopt.dosopttemplate.presentation.doandroid import androidx.lifecycle.ViewModel -import org.sopt.dosopttemplate.API.ResponseData -import org.sopt.dosopttemplate.data.Profile class DOAndroidViewModel : ViewModel() { - private var _responseDataList: MutableList = mutableListOf() - - fun getResponseDataList(): MutableList = _responseDataList - fun initResponseDataList(responseDataList: List?) { - if (responseDataList.isNullOrEmpty()) return - for (data in responseDataList) { - _responseDataList.add(convertDataToProfile(data)) - } - } - - private fun convertDataToProfile(data: ResponseData): Profile = - Profile(getName(data), data.email, data.avatar, data.id) - - private fun getName(data: ResponseData): String = data.first_name + " " + data.last_name } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/editMyPage/EditMyPageViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/editMyPage/EditMyPageViewModel.kt index ff7292a..b7feb73 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/editMyPage/EditMyPageViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/editMyPage/EditMyPageViewModel.kt @@ -14,12 +14,10 @@ class EditMyPageViewModel : ViewModel() { val id: MutableLiveData = MutableLiveData() fun setNewProfile(): Profile { - if (!(mbti.value.isNullOrEmpty())) profile.MBTI = mbti.value.toString() - if (!(musicTitle.value.isNullOrEmpty())) profile.musicTitle = musicTitle.value.toString() - if (!(musicArtist.value.isNullOrEmpty())) profile.musicArtist = musicArtist.value.toString() - if (!(intro.value.isNullOrEmpty())) profile.intro = intro.value.toString() - profile.setMusic() -// logProfile(profile) + if (!(mbti.value.isNullOrEmpty())) profile.setMbti(mbti.value.toString()) + if (!(musicTitle.value.isNullOrEmpty()) && !(musicArtist.value.isNullOrEmpty())) + profile.setMusic(musicTitle.value.toString(), musicArtist.value.toString()) + if (!(intro.value.isNullOrEmpty())) profile.setIntro(intro.value.toString()) return profile } @@ -29,7 +27,7 @@ class EditMyPageViewModel : ViewModel() { fun initPage() { id.value = profile.id.toString() - nickName.value = profile.name + nickName.value = profile.nickname } companion object { diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/FriendViewHolder.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/FriendViewHolder.kt index c470248..73ae2d6 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/FriendViewHolder.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/FriendViewHolder.kt @@ -12,10 +12,10 @@ class FriendViewHolder(private val binding: ItemFriendBinding) : ivProfile.setImageResource(friendData.profileImage) else { Glide.with(binding.root) - .load(friendData.avatar) + .load(friendData.reqresData?.avatar) .into(ivProfile) } - tvName.text = friendData.name - tvMusic.text = friendData.music?.string + tvName.text = friendData.nickname + tvMusic.text = friendData.getMusic() } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt index 91addf2..eb397e0 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt @@ -1,14 +1,25 @@ package org.sopt.dosopttemplate.presentation.home +import android.app.Activity import android.os.Bundle +import android.util.Log import android.view.View import androidx.fragment.app.viewModels +import org.sopt.dosopttemplate.API.ResponseGetFollwerDto +import org.sopt.dosopttemplate.API.ServicePool import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.databinding.FragmentHomeBinding +import org.sopt.dosopttemplate.util.ToastMaker import org.sopt.dosopttemplate.util.binding.BindingFragment +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response class HomeFragment : BindingFragment(R.layout.fragment_home) { private val viewModel by viewModels() + private var _profileAdapter: ProfileAdapter? = null + val profileAdapter: ProfileAdapter + get() = requireNotNull(_profileAdapter) { "profileAdapter not initialized" } fun newInstance(): HomeFragment { val args = Bundle() @@ -19,9 +30,36 @@ class HomeFragment : BindingFragment(R.layout.fragment_home override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val profileAdapter = ProfileAdapter(requireContext()) + _profileAdapter = ProfileAdapter(requireContext()) binding.lifecycleOwner = this binding.rvProfiles.adapter = profileAdapter + getFollowerList() + } + + private fun getFollowerList() { + ServicePool.followerService.getFollowerList() + .enqueue(object : Callback { + override fun onResponse( + call: Call, + response: Response + ) { + if (response.isSuccessful) { + setProfileListFromResponse(response) + } + } + + override fun onFailure(call: Call, t: Throwable) { + Log.v("serverError", t.toString()) + val context = context as Activity + ToastMaker.makeToast(context, getString(R.string.serverError)) + } + }) + } + + private fun setProfileListFromResponse(response: Response) { + val responseBody = response.body() + val data = responseBody?.data + viewModel.initResponseDataList(data) profileAdapter.setProfileList(viewModel.getMockProfileLIst()) } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt index 5e7b158..f62c939 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt @@ -1,6 +1,7 @@ package org.sopt.dosopttemplate.presentation.home import androidx.lifecycle.ViewModel +import org.sopt.dosopttemplate.API.ResponseData import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.data.Profile @@ -8,95 +9,42 @@ class HomeViewModel : ViewModel() { val mockProfileList = mutableListOf( Profile( profileImage = R.drawable.img_monkey, - name = "경지현", - musicTitle = "행복했던 날들 이었다", - musicArtist = "데이식스", + id = "jihyune_hi", + nickname = "경지현", + musicTitle = "숲", + musicArtist = "최유리", type = ME, - MBTI = "INTJ", - intro = "hello everyone", - id = "jihyune_hi" - ), - Profile( - profileImage = R.drawable.img_wolf, - name = "박강희", - musicTitle = "Gorgeous", - musicArtist = "골져스", - type = FRIEND - ), - Profile( - profileImage = R.drawable.img_wolf, - name = "이삭", - musicTitle = "봄아", - musicArtist = "홍이삭", - type = FRIEND - ), - Profile( - profileImage = R.drawable.img_wolf, - name = "조세연", - musicTitle = "세상아 덤벼라", - musicArtist = "귀요미", - type = FRIEND - ), - Profile( - profileImage = R.drawable.img_wolf, - name = "박동민", - musicTitle = "일본 재밌었어...?", - musicArtist = "JJ", - type = FRIEND - ), - Profile( - profileImage = R.drawable.img_wolf, - name = "김이름", - musicTitle = "노래", - musicArtist = "Zion.T", - type = FRIEND - ), - Profile( - profileImage = R.drawable.img_wolf, - name = "경대승", - musicTitle = "가시리", - musicArtist = "작자미상", - type = FRIEND - ), - Profile( - profileImage = R.drawable.img_wolf, - name = "경전철", - musicTitle = "슈웅", - musicArtist = "피웅", - type = FRIEND - ), - Profile( - profileImage = R.drawable.img_wolf, - name = "이태희", - musicTitle = "Hello", - musicArtist = "Adele", - type = FRIEND - ), - Profile( - profileImage = R.drawable.img_wolf, - name = "이 름", - musicTitle = "Song", - musicArtist = "Famous Artist", - type = FRIEND - ), - Profile( - profileImage = R.drawable.img_wolf, - name = "성 함", - musicTitle = "아리랑", - musicArtist = "작자미상", - type = FRIEND - ), - + mbti = "intj", + intro = "안녕하세요" ) + ) fun setNewProfile(newProfile: Profile, index: Int) { mockProfileList[index] = newProfile } fun getProfile(index: Int): Profile = mockProfileList[index] - fun getMockProfileLIst() = mockProfileList + // fun getResponseDataList(): MutableList = _responseDataList + fun initResponseDataList(responseDataList: List?) { + if (responseDataList.isNullOrEmpty()) return + for (i in 0..responseDataList.lastIndex) { + if ((i + 1) > mockProfileList.lastIndex) + addNewProfile(convertDataToProfile(responseDataList[i])) + setNewProfile(convertDataToProfile(responseDataList[i]), i + 1) + } + } + + private fun addNewProfile(profile: Profile) { + mockProfileList.add(profile) + } + + private fun convertDataToProfile(data: ResponseData): Profile = + Profile(getName(data), data.email, data.avatar, data.id) + + private fun getName(data: ResponseData): String = data.first_name + " " + data.last_name + companion object { const val ME = 1 const val FRIEND = 0 diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/MyProfileViewHolder.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/MyProfileViewHolder.kt index 0206111..2330b08 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/MyProfileViewHolder.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/MyProfileViewHolder.kt @@ -9,6 +9,6 @@ class MyProfileViewHolder(private val binding: ItemMyprofileBinding) : fun onBind(myProfileData: Profile) { if (myProfileData.profileImage != null) binding.ivProfile.setImageResource(myProfileData.profileImage) - binding.tvName.text = myProfileData.name + binding.tvName.text = myProfileData.nickname } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt index 5963bdd..fee25ac 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt @@ -66,11 +66,8 @@ class LoginActivity : AppCompatActivity() { override fun onFailure(call: Call, t: Throwable) { makeToast(this@LoginActivity, getString(R.string.serverError)) } - }) - } - } private fun processLogin(response: Response) { diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/myPage/MyPageViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/myPage/MyPageViewModel.kt index d725f7b..e7e2237 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/myPage/MyPageViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/myPage/MyPageViewModel.kt @@ -15,10 +15,10 @@ class MyPageViewModel : ViewModel() { val intro: MutableLiveData = MutableLiveData() private fun setMyPage() { id = profile.id.toString() - nickName.value = profile.name - mbti.value = profile.MBTI.toString() + nickName.value = profile.nickname + mbti.value = profile.getMbti() music.value = profile.getMusic() - intro.value = profile.intro.toString() + intro.value = profile.getIntro() } fun setNewProfileAndSetPage(newProfile: Profile) { From eb1e6b0291bd0418a3b9ff238d638769d5403138 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Thu, 16 Nov 2023 14:26:02 +0900 Subject: [PATCH 05/16] [refact] refactoring --- .../dosopttemplate/presentation/home/HomeFragment.kt | 3 +-- .../presentation/signup/SignUpActivity.kt | 10 +--------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt index eb397e0..54a5401 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt @@ -50,8 +50,7 @@ class HomeFragment : BindingFragment(R.layout.fragment_home override fun onFailure(call: Call, t: Throwable) { Log.v("serverError", t.toString()) - val context = context as Activity - ToastMaker.makeToast(context, getString(R.string.serverError)) + ToastMaker.makeToast(context as Activity, getString(R.string.serverError)) } }) } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt index bcd1b49..323f286 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt @@ -67,17 +67,9 @@ class SingUpActivity : AppCompatActivity() { private fun processSignUp() { makeToast(this, "회원가입 완료!") - sendDataToLoginActivity() -// TODO: saveSignUpData() - } - - private fun sendDataToLoginActivity() { - with(intent) { - val signUpData = viewModel.createSignUpInfo() - putExtra(SIGNUPINFO, signUpData) - } setResult(RESULT_OK, intent) finish() +// TODO: saveSignUpData() } companion object { From a45a98e231b37a1a38a618e20f08953450039fd0 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 28 Nov 2023 10:49:26 +0900 Subject: [PATCH 06/16] [chore] reformatting --- app/build.gradle | 9 --- .../dosopttemplate/API/FollowerService.kt | 10 --- .../dosopttemplate/{API => api}/APIFactory.kt | 2 +- .../{API => api}/AuthService.kt | 2 +- .../sopt/dosopttemplate/api/AuthViewModel.kt | 46 ++++++++++++++ .../dosopttemplate/api/FollowerService.kt | 12 ++++ .../{API => api}/RequestLoginDto.kt | 2 +- .../{API => api}/RequestSignUpDto.kt | 2 +- .../{API => api}/ResponseGetFollwerDto.kt | 2 +- .../{API => api}/ResponseLoginDto.kt | 2 +- .../{API => api}/SignUpResponse.kt | 2 +- .../presentation/home/HomeFragment.kt | 4 +- .../presentation/home/HomeViewModel.kt | 2 +- .../presentation/login/LoginActivity.kt | 63 +++++++++++++------ .../presentation/signup/SignUpActivity.kt | 4 +- app/src/main/res/layout/activity_login.xml | 7 ++- 16 files changed, 119 insertions(+), 52 deletions(-) delete mode 100644 app/src/main/java/org/sopt/dosopttemplate/API/FollowerService.kt rename app/src/main/java/org/sopt/dosopttemplate/{API => api}/APIFactory.kt (97%) rename app/src/main/java/org/sopt/dosopttemplate/{API => api}/AuthService.kt (90%) create mode 100644 app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt create mode 100644 app/src/main/java/org/sopt/dosopttemplate/api/FollowerService.kt rename app/src/main/java/org/sopt/dosopttemplate/{API => api}/RequestLoginDto.kt (86%) rename app/src/main/java/org/sopt/dosopttemplate/{API => api}/RequestSignUpDto.kt (88%) rename app/src/main/java/org/sopt/dosopttemplate/{API => api}/ResponseGetFollwerDto.kt (96%) rename app/src/main/java/org/sopt/dosopttemplate/{API => api}/ResponseLoginDto.kt (88%) rename app/src/main/java/org/sopt/dosopttemplate/{API => api}/SignUpResponse.kt (73%) diff --git a/app/build.gradle b/app/build.gradle index 78687bb..5c39981 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,12 +16,9 @@ android { targetSdk 33 versionCode 1 versionName "1.0" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" buildConfigField "String", "REQRES_BASE_URL", properties["reqres.base.url"] buildConfigField "String", "AUTH_BASE_URL", properties["base.url"] - - } buildFeatures { viewBinding true @@ -41,7 +38,6 @@ android { kotlinOptions { jvmTarget = '11' } - } dependencies { @@ -59,14 +55,9 @@ dependencies { implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1' implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0' - -// define a BOM and its version implementation(platform("com.squareup.okhttp3:okhttp-bom:4.10.0")) - -// define any required OkHttp artifacts without version implementation("com.squareup.okhttp3:okhttp") implementation("com.squareup.okhttp3:logging-interceptor") - implementation 'com.github.bumptech.glide:glide:4.12.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/FollowerService.kt b/app/src/main/java/org/sopt/dosopttemplate/API/FollowerService.kt deleted file mode 100644 index 48090c8..0000000 --- a/app/src/main/java/org/sopt/dosopttemplate/API/FollowerService.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.sopt.dosopttemplate.API - -import retrofit2.Call -import retrofit2.http.Body -import retrofit2.http.GET - -interface FollowerService { - @GET("https://reqres.in/api/users?page=2") - fun getFollowerList(): Call -} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/APIFactory.kt b/app/src/main/java/org/sopt/dosopttemplate/api/APIFactory.kt similarity index 97% rename from app/src/main/java/org/sopt/dosopttemplate/API/APIFactory.kt rename to app/src/main/java/org/sopt/dosopttemplate/api/APIFactory.kt index 4e62692..364d248 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/API/APIFactory.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/APIFactory.kt @@ -1,4 +1,4 @@ -package org.sopt.dosopttemplate.API +package org.sopt.dosopttemplate.api import android.util.Log import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/AuthService.kt b/app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt similarity index 90% rename from app/src/main/java/org/sopt/dosopttemplate/API/AuthService.kt rename to app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt index bdb3b5c..029cd78 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/API/AuthService.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt @@ -1,4 +1,4 @@ -package org.sopt.dosopttemplate.API +package org.sopt.dosopttemplate.api import retrofit2.Call import retrofit2.http.Body diff --git a/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt new file mode 100644 index 0000000..cb99492 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt @@ -0,0 +1,46 @@ +package org.sopt.dosopttemplate.api// 보시면 알겠지만 LiveData는 lifecycle 라이브러리에 들어있습니다! +// 서비스 객체를 따로 만들지 않고 바로 불러왔습니다. +// 언제나 그렇듯 Call과 Callback은 Retrofit2의 것을 사용해야 합니다. okhttp 아님 주의! +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import org.sopt.dosopttemplate.api.ServicePool.authService +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +class AuthViewModel : ViewModel() { + // MutableLiveData를 사용하여 login result 객체를 생성합니다. + + private val _loginResult: MutableLiveData = MutableLiveData() + val loginResult: MutableLiveData get() = _loginResult + private val _loginSuccess: MutableLiveData = MutableLiveData() + val loginSuccess: MutableLiveData get() = _loginSuccess + + val isLoginButtonClicked: MutableLiveData = MutableLiveData(false) + + fun login(id: String, password: String) { + authService.login(RequestLoginDto(id, password)) + .enqueue(object : Callback { + override fun onResponse( + call: Call, + response: Response, + ) { + if (response.isSuccessful) { + _loginResult.value = response.body() + _loginSuccess.value = true + } else { + // TODO: 에러 처리 로직 + _loginSuccess.value = false + } + } + + override fun onFailure(call: Call, t: Throwable) { + // TODO: 에러 처리 로직 + } + }) + } + + fun onLoginButtonClick() { + isLoginButtonClicked.value = true + } +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/api/FollowerService.kt b/app/src/main/java/org/sopt/dosopttemplate/api/FollowerService.kt new file mode 100644 index 0000000..54b59f3 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/api/FollowerService.kt @@ -0,0 +1,12 @@ +package org.sopt.dosopttemplate.api + +import org.sopt.dosopttemplate.BuildConfig +import retrofit2.Call +import retrofit2.http.GET + +private const val REQURES_BASE_URL = BuildConfig.REQRES_BASE_URL + +interface FollowerService { + @GET(REQURES_BASE_URL) + fun getFollowerList(): Call +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/RequestLoginDto.kt b/app/src/main/java/org/sopt/dosopttemplate/api/RequestLoginDto.kt similarity index 86% rename from app/src/main/java/org/sopt/dosopttemplate/API/RequestLoginDto.kt rename to app/src/main/java/org/sopt/dosopttemplate/api/RequestLoginDto.kt index da17e2c..660867d 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/API/RequestLoginDto.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/RequestLoginDto.kt @@ -1,4 +1,4 @@ -package org.sopt.dosopttemplate.API +package org.sopt.dosopttemplate.api import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/RequestSignUpDto.kt b/app/src/main/java/org/sopt/dosopttemplate/api/RequestSignUpDto.kt similarity index 88% rename from app/src/main/java/org/sopt/dosopttemplate/API/RequestSignUpDto.kt rename to app/src/main/java/org/sopt/dosopttemplate/api/RequestSignUpDto.kt index 63c2a32..d924cd1 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/API/RequestSignUpDto.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/RequestSignUpDto.kt @@ -1,4 +1,4 @@ -package org.sopt.dosopttemplate.API +package org.sopt.dosopttemplate.api import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/ResponseGetFollwerDto.kt b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollwerDto.kt similarity index 96% rename from app/src/main/java/org/sopt/dosopttemplate/API/ResponseGetFollwerDto.kt rename to app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollwerDto.kt index b2fdb05..c0ed6ad 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/API/ResponseGetFollwerDto.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollwerDto.kt @@ -1,4 +1,4 @@ -package org.sopt.dosopttemplate.API +package org.sopt.dosopttemplate.api import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/ResponseLoginDto.kt b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseLoginDto.kt similarity index 88% rename from app/src/main/java/org/sopt/dosopttemplate/API/ResponseLoginDto.kt rename to app/src/main/java/org/sopt/dosopttemplate/api/ResponseLoginDto.kt index be752f3..d5d31a2 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/API/ResponseLoginDto.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseLoginDto.kt @@ -1,4 +1,4 @@ -package org.sopt.dosopttemplate.API +package org.sopt.dosopttemplate.api import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/app/src/main/java/org/sopt/dosopttemplate/API/SignUpResponse.kt b/app/src/main/java/org/sopt/dosopttemplate/api/SignUpResponse.kt similarity index 73% rename from app/src/main/java/org/sopt/dosopttemplate/API/SignUpResponse.kt rename to app/src/main/java/org/sopt/dosopttemplate/api/SignUpResponse.kt index bb6f810..167f8ff 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/API/SignUpResponse.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/SignUpResponse.kt @@ -1,4 +1,4 @@ -package org.sopt.dosopttemplate.API +package org.sopt.dosopttemplate.api sealed class SignupResponse { data class ResponseSignUpDto( diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt index 54a5401..546526d 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt @@ -5,8 +5,8 @@ import android.os.Bundle import android.util.Log import android.view.View import androidx.fragment.app.viewModels -import org.sopt.dosopttemplate.API.ResponseGetFollwerDto -import org.sopt.dosopttemplate.API.ServicePool +import org.sopt.dosopttemplate.api.ResponseGetFollwerDto +import org.sopt.dosopttemplate.api.ServicePool import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.databinding.FragmentHomeBinding import org.sopt.dosopttemplate.util.ToastMaker diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt index f62c939..0bbde64 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt @@ -1,7 +1,7 @@ package org.sopt.dosopttemplate.presentation.home import androidx.lifecycle.ViewModel -import org.sopt.dosopttemplate.API.ResponseData +import org.sopt.dosopttemplate.api.ResponseData import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.data.Profile diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt index fee25ac..a007ab4 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt @@ -1,5 +1,6 @@ package org.sopt.dosopttemplate.presentation.login +import org.sopt.dosopttemplate.api.AuthViewModel import android.content.Intent import android.os.Bundle import androidx.activity.result.ActivityResultLauncher @@ -7,9 +8,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil -import org.sopt.dosopttemplate.API.RequestLoginDto -import org.sopt.dosopttemplate.API.ResponseLoginDto -import org.sopt.dosopttemplate.API.ServicePool.authService +import org.sopt.dosopttemplate.api.ResponseLoginDto import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.data.SignUpInfo import org.sopt.dosopttemplate.databinding.ActivityLoginBinding @@ -17,13 +16,13 @@ import org.sopt.dosopttemplate.presentation.home.HomeActivity import org.sopt.dosopttemplate.presentation.signup.SingUpActivity import org.sopt.dosopttemplate.util.ToastMaker.makeToast import org.sopt.dosopttemplate.util.getParcelable -import retrofit2.Call import retrofit2.Response class LoginActivity : AppCompatActivity() { private lateinit var binding: ActivityLoginBinding private lateinit var signUpInfo: SignUpInfo private val viewModel by viewModels() + private val authViewModel by viewModels() private lateinit var resultLauncher: ActivityResultLauncher override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -33,6 +32,7 @@ class LoginActivity : AppCompatActivity() { getSignUpInfo() login() clickSignUpBtn() + observeLoginResult() } private fun getSignUpInfo() { @@ -48,25 +48,31 @@ class LoginActivity : AppCompatActivity() { } private fun login() { - val id = binding.etId.text.toString() - val password = binding.etPassword.text.toString() binding.btLogin.setOnClickListener { - authService.login(RequestLoginDto(id, password)) - .enqueue(object : retrofit2.Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (response.isSuccessful) { - processLogin(response) - } - } + val id = binding.etId.text.toString() + val password = binding.etPassword.text.toString() - override fun onFailure(call: Call, t: Throwable) { - makeToast(this@LoginActivity, getString(R.string.serverError)) - } - }) + authViewModel.login( + id = id, + password = password, + ) + +// authService.login(RequestLoginDto(id, password)) +// .enqueue(object : retrofit2.Callback { +// override fun onResponse( +// call: Call, +// response: Response, +// ) { +// if (response.isSuccessful) { +// processLogin(response) +// } +// } +// +// override fun onFailure(call: Call, t: Throwable) { +// makeToast(this@LoginActivity, getString(R.string.serverError)) +// } +// }) } } @@ -93,5 +99,22 @@ class LoginActivity : AppCompatActivity() { } } + private fun observeLoginResult() { + authViewModel.loginSuccess.observe(this) { + // 여기서 it은 loginSucess 객체의 value입니다. + if (it) { + makeToast( + this@LoginActivity,"로그인 성공" + ) + goToMainPage() + } else { + makeToast( + this@LoginActivity, + "로그인 실패" + ) + } + } + } + } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt index 323f286..1298d5e 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt @@ -4,8 +4,8 @@ import android.os.Bundle import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil -import org.sopt.dosopttemplate.API.RequestSignUpDto -import org.sopt.dosopttemplate.API.ServicePool.authService +import org.sopt.dosopttemplate.api.RequestSignUpDto +import org.sopt.dosopttemplate.api.ServicePool.authService import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.databinding.ActivitySignupBinding import org.sopt.dosopttemplate.util.ToastMaker.makeToast diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 5f4ca7c..b8072be 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -10,7 +10,11 @@ - + + + + Date: Tue, 28 Nov 2023 10:52:22 +0900 Subject: [PATCH 07/16] [chore] rename ResponseGetFollowerDto --- ...ResponseGetFollwerDto.kt => ResponseGetFollowerDto.kt} | 8 ++++---- .../dosopttemplate/presentation/home/HomeViewModel.kt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) rename app/src/main/java/org/sopt/dosopttemplate/api/{ResponseGetFollwerDto.kt => ResponseGetFollowerDto.kt} (88%) diff --git a/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollwerDto.kt b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollowerDto.kt similarity index 88% rename from app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollwerDto.kt rename to app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollowerDto.kt index c0ed6ad..566a3db 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollwerDto.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollowerDto.kt @@ -10,9 +10,9 @@ data class ResponseData( @SerialName("email") val email: String, @SerialName("first_name") - val first_name: String, + val firstName: String, @SerialName("last_name") - val last_name: String, + val lastName: String, @SerialName("avatar") val avatar: String, ) @@ -30,11 +30,11 @@ data class ResponseGetFollwerDto( @SerialName("page") val page: Int, @SerialName("per_page") - val per_page: Int, + val perPage: Int, @SerialName("total") val total: Int, @SerialName("total_pages") - val total_pages: Int, + val totalPages: Int, @SerialName("data") val data: List, @SerialName("support") diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt index 0bbde64..24316a6 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt @@ -43,7 +43,7 @@ class HomeViewModel : ViewModel() { private fun convertDataToProfile(data: ResponseData): Profile = Profile(getName(data), data.email, data.avatar, data.id) - private fun getName(data: ResponseData): String = data.first_name + " " + data.last_name + private fun getName(data: ResponseData): String = data.firstName + " " + data.lastName companion object { const val ME = 1 From 7d7c35a226c3180bd99cc3b4965087cb183d8948 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 28 Nov 2023 10:53:19 +0900 Subject: [PATCH 08/16] =?UTF-8?q?[chore]=20data=20class=20=EC=88=9C?= =?UTF-8?q?=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/ResponseGetFollowerDto.kt | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollowerDto.kt b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollowerDto.kt index 566a3db..8f5af3c 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollowerDto.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollowerDto.kt @@ -3,28 +3,6 @@ package org.sopt.dosopttemplate.api import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -@Serializable -data class ResponseData( - @SerialName("id") - val id: Int, - @SerialName("email") - val email: String, - @SerialName("first_name") - val firstName: String, - @SerialName("last_name") - val lastName: String, - @SerialName("avatar") - val avatar: String, -) - -@Serializable -data class ResponseSupport( - @SerialName("url") - val url: String, - @SerialName("text") - val text: String -) - @Serializable data class ResponseGetFollwerDto( @SerialName("page") @@ -39,4 +17,26 @@ data class ResponseGetFollwerDto( val data: List, @SerialName("support") val support: ResponseSupport -) \ No newline at end of file +) + +@Serializable +data class ResponseSupport( + @SerialName("url") + val url: String, + @SerialName("text") + val text: String +) + +@Serializable +data class ResponseData( + @SerialName("id") + val id: Int, + @SerialName("email") + val email: String, + @SerialName("first_name") + val firstName: String, + @SerialName("last_name") + val lastName: String, + @SerialName("avatar") + val avatar: String, +) From 387932c8a7e964da9efe642685a3cbcdd700f581 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 28 Nov 2023 10:54:32 +0900 Subject: [PATCH 09/16] =?UTF-8?q?[chore]=20=EC=A3=BC=EC=84=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/sopt/dosopttemplate/data/Profile.kt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt b/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt index 1b076fa..f7dd199 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt @@ -30,10 +30,7 @@ data class Profile( val profileImage: Int?, val id: String?, var nickname: String, -// var music: Music?, val type: Int, -// var MBTI: String?, -// var intro: String?, val reqresData: ReqresData?, val subInfo: subInfo ) : Parcelable { @@ -61,7 +58,6 @@ data class Profile( R.drawable.img_monkey, null, nickname, -// null, type, null, subInfo(null, mbti, intro) @@ -69,7 +65,7 @@ data class Profile( constructor( profileImage: Int, - id : String, + id: String, nickname: String, musicTitle: String, musicArtist: String, @@ -80,10 +76,7 @@ data class Profile( profileImage, id, nickname, -// Music(musicTitle, musicArtist), type, -// MBTI, -// intro, null, subInfo(Music(musicTitle, musicArtist), mbti, intro) ) @@ -97,7 +90,6 @@ data class Profile( null, null, nickname, -// null, FRIEND, ReqresData(email, avatar, idInt), subInfo(null) From aa1f61b32d770221a675b062f02747d70f1eada5 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 28 Nov 2023 11:38:49 +0900 Subject: [PATCH 10/16] [chore] reformat --- .../presentation/editMyPage/EditMyPageActivity.kt | 10 +++++----- .../presentation/home/HomeViewModel.kt | 14 ++++++++------ .../presentation/login/LoginActivity.kt | 2 +- app/src/main/res/layout/activity_home.xml | 1 - 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/editMyPage/EditMyPageActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/editMyPage/EditMyPageActivity.kt index 2f5b637..598d8ab 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/editMyPage/EditMyPageActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/editMyPage/EditMyPageActivity.kt @@ -12,7 +12,7 @@ import org.sopt.dosopttemplate.presentation.home.HomeViewModel class EditMyPageActivity : AppCompatActivity() { private lateinit var binding: ActivityEditmypageBinding private val homeViewModel by viewModels() - private val viewModel by viewModels() + private val editMyPageViewModel by viewModels() private lateinit var myProfile: Profile override fun onCreate(savedInstanceState: Bundle?) { @@ -20,9 +20,9 @@ class EditMyPageActivity : AppCompatActivity() { binding = DataBindingUtil.setContentView(this, R.layout.activity_editmypage) binding.lifecycleOwner = this myProfile = homeViewModel.getProfile(0) - binding.viewModel = viewModel - viewModel.setProfile(myProfile) - viewModel.initPage() + binding.viewModel = editMyPageViewModel + editMyPageViewModel.setProfile(myProfile) + editMyPageViewModel.initPage() clickFinishButton() } @@ -34,7 +34,7 @@ class EditMyPageActivity : AppCompatActivity() { } private fun setNewProfile() { - myProfile = viewModel.setNewProfile() + myProfile = editMyPageViewModel.setNewProfile() } private fun getNewProfileAndParseIntent() { diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt index 24316a6..4acdac7 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeViewModel.kt @@ -1,8 +1,8 @@ package org.sopt.dosopttemplate.presentation.home import androidx.lifecycle.ViewModel -import org.sopt.dosopttemplate.api.ResponseData import org.sopt.dosopttemplate.R +import org.sopt.dosopttemplate.api.ResponseData import org.sopt.dosopttemplate.data.Profile class HomeViewModel : ViewModel() { @@ -40,14 +40,16 @@ class HomeViewModel : ViewModel() { mockProfileList.add(profile) } - private fun convertDataToProfile(data: ResponseData): Profile = - Profile(getName(data), data.email, data.avatar, data.id) - - private fun getName(data: ResponseData): String = data.firstName + " " + data.lastName + private fun convertDataToProfile(data: ResponseData): Profile { + val fullName = data.firstName + " " + data.lastName + return Profile(fullName, data.email, data.avatar, data.id) + } companion object { const val ME = 1 const val FRIEND = 0 } -} \ No newline at end of file +} + + diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt index a007ab4..7af203c 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt @@ -77,7 +77,7 @@ class LoginActivity : AppCompatActivity() { } private fun processLogin(response: Response) { - val data: ResponseLoginDto = response.body()!! + val data: ResponseLoginDto = response.body() ?: error("") val userId = data.id makeToast( this@LoginActivity, diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 9e40c84..b4eabf6 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -15,7 +15,6 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - Date: Tue, 28 Nov 2023 11:39:57 +0900 Subject: [PATCH 11/16] =?UTF-8?q?[chore]=20requireNotNull=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=EB=B3=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/login/LoginActivity.kt | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt index 7af203c..e77b076 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt @@ -1,6 +1,5 @@ package org.sopt.dosopttemplate.presentation.login -import org.sopt.dosopttemplate.api.AuthViewModel import android.content.Intent import android.os.Bundle import androidx.activity.result.ActivityResultLauncher @@ -8,8 +7,9 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil -import org.sopt.dosopttemplate.api.ResponseLoginDto import org.sopt.dosopttemplate.R +import org.sopt.dosopttemplate.api.AuthViewModel +import org.sopt.dosopttemplate.api.ResponseLoginDto import org.sopt.dosopttemplate.data.SignUpInfo import org.sopt.dosopttemplate.databinding.ActivityLoginBinding import org.sopt.dosopttemplate.presentation.home.HomeActivity @@ -50,13 +50,13 @@ class LoginActivity : AppCompatActivity() { private fun login() { binding.btLogin.setOnClickListener { - val id = binding.etId.text.toString() - val password = binding.etPassword.text.toString() + val id = binding.etId.text.toString() + val password = binding.etPassword.text.toString() - authViewModel.login( - id = id, - password = password, - ) + authViewModel.login( + id = id, + password = password, + ) // authService.login(RequestLoginDto(id, password)) // .enqueue(object : retrofit2.Callback { @@ -77,7 +77,8 @@ class LoginActivity : AppCompatActivity() { } private fun processLogin(response: Response) { - val data: ResponseLoginDto = response.body() ?: error("") + val data: ResponseLoginDto = + requireNotNull(response.body()) { "Response body should not be null" } val userId = data.id makeToast( this@LoginActivity, @@ -104,14 +105,14 @@ class LoginActivity : AppCompatActivity() { // 여기서 it은 loginSucess 객체의 value입니다. if (it) { makeToast( - this@LoginActivity,"로그인 성공" + this@LoginActivity, "로그인 성공" ) goToMainPage() } else { makeToast( - this@LoginActivity, - "로그인 실패" - ) + this@LoginActivity, + "로그인 실패" + ) } } } From 3da6983090fd8a6db023af4db71184647bc4d194 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 28 Nov 2023 11:55:21 +0900 Subject: [PATCH 12/16] [feat] data binding --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5c39981..4e88662 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,13 +42,13 @@ android { dependencies { implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2" - implementation "androidx.fragment:fragment-ktx:1.6.1" + implementation "androidx.fragment:fragment-ktx:1.6.2" implementation "androidx.activity:activity-ktx:1.6.1" implementation 'androidx.core:core-ktx:1.8.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.databinding:databinding-runtime:7.1.2' + implementation 'androidx.databinding:databinding-runtime:8.1.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' From f8c7af2c0a4318dd3fcd64625e320ae2b0750c05 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 28 Nov 2023 19:12:24 +0900 Subject: [PATCH 13/16] =?UTF-8?q?[feat]=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/dosopttemplate/api/AuthService.kt | 9 +- .../sopt/dosopttemplate/api/AuthViewModel.kt | 84 ++++++++++++++----- .../presentation/login/LoginActivity.kt | 5 +- .../presentation/signup/SignUpActivity.kt | 43 +++++----- app/src/main/res/layout/activity_login.xml | 15 +++- app/src/main/res/values/strings.xml | 3 + 6 files changed, 106 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt b/app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt index 029cd78..02e38e5 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt @@ -1,19 +1,20 @@ package org.sopt.dosopttemplate.api import retrofit2.Call +import retrofit2.Response import retrofit2.http.Body import retrofit2.http.POST interface AuthService { @POST("api/v1/members/sign-in") - fun login( + suspend fun login( @Body request: RequestLoginDto, - ): Call + ): Response @POST("api/v1/members") - fun signUp( + suspend fun signUp( @Body request: RequestSignUpDto, - ): Call + ): Response } diff --git a/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt index cb99492..1979263 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt @@ -3,43 +3,87 @@ package org.sopt.dosopttemplate.api// 보시면 알겠지만 LiveData는 lifecyc // 언제나 그렇듯 Call과 Callback은 Retrofit2의 것을 사용해야 합니다. okhttp 아님 주의! import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch import org.sopt.dosopttemplate.api.ServicePool.authService -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response class AuthViewModel : ViewModel() { // MutableLiveData를 사용하여 login result 객체를 생성합니다. private val _loginResult: MutableLiveData = MutableLiveData() val loginResult: MutableLiveData get() = _loginResult + private val _loginSuccess: MutableLiveData = MutableLiveData() val loginSuccess: MutableLiveData get() = _loginSuccess + private val _signUpResult: MutableLiveData = MutableLiveData() + val signUpResult: MutableLiveData get() = _signUpResult + + private val _signUpSuccess: MutableLiveData = MutableLiveData() + val signUpSuccess: MutableLiveData get() = _signUpSuccess + val isLoginButtonClicked: MutableLiveData = MutableLiveData(false) - fun login(id: String, password: String) { - authService.login(RequestLoginDto(id, password)) - .enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (response.isSuccessful) { - _loginResult.value = response.body() - _loginSuccess.value = true - } else { - // TODO: 에러 처리 로직 - _loginSuccess.value = false - } + val _id: MutableLiveData = MutableLiveData() + val id: String get() = _id.value?:"" + val _password: MutableLiveData = MutableLiveData() + val password: String get() = _password.value?:"" + + fun login() { +// authService.login(RequestLoginDto(id, password)) +// .enqueue(object : Callback { +// override fun onResponse( +// call: Call, +// response: Response, +// ) { +// if (response.isSuccessful) { +// _loginResult.value = response.body() +// _loginSuccess.value = true +// } else { +// // TODO: 에러 처리 로직 +// _loginSuccess.value = false +// } +// } +// +// override fun onFailure(call: Call, t: Throwable) { +// // TODO: 에러 처리 로직 +// } +// }) + + viewModelScope.launch { + kotlin.runCatching { + authService.login(RequestLoginDto(id, password)) + }.onSuccess { + if (it.isSuccessful) { + loginResult.value = it.body() + loginSuccess.value = true + } else { + loginSuccess.value = false } + }.onFailure { + // 에러 처리 + } + } + } - override fun onFailure(call: Call, t: Throwable) { - // TODO: 에러 처리 로직 + fun signUp(id: String, nickname: String, password: String) { + viewModelScope.launch { + kotlin.runCatching { + authService.signUp(RequestSignUpDto(id, nickname, password)) + }.onSuccess { + if (it.isSuccessful) { + signUpResult.value = it.body() + signUpSuccess.value = true + } else { + signUpSuccess.value = false } - }) + }.onFailure { + // 에러 처리 + } + } } + fun onLoginButtonClick() { isLoginButtonClicked.value = true } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt index e77b076..a3106d9 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt @@ -29,6 +29,7 @@ class LoginActivity : AppCompatActivity() { binding = DataBindingUtil.setContentView(this, R.layout.activity_login) binding.lifecycleOwner = this binding.viewModel = viewModel + binding.authViewModel = authViewModel getSignUpInfo() login() clickSignUpBtn() @@ -54,8 +55,8 @@ class LoginActivity : AppCompatActivity() { val password = binding.etPassword.text.toString() authViewModel.login( - id = id, - password = password, +// id = id, +// password = password, ) // authService.login(RequestLoginDto(id, password)) diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt index 1298d5e..7da4e9e 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt @@ -7,6 +7,7 @@ import androidx.databinding.DataBindingUtil import org.sopt.dosopttemplate.api.RequestSignUpDto import org.sopt.dosopttemplate.api.ServicePool.authService import org.sopt.dosopttemplate.R +import org.sopt.dosopttemplate.api.AuthViewModel import org.sopt.dosopttemplate.databinding.ActivitySignupBinding import org.sopt.dosopttemplate.util.ToastMaker.makeToast import retrofit2.Call @@ -15,23 +16,24 @@ import retrofit2.Response class SingUpActivity : AppCompatActivity() { private lateinit var binding: ActivitySignupBinding - private val viewModel by viewModels() + private val signUpViewModel by viewModels() + private val authViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_signup) binding.lifecycleOwner = this - binding.viewModel = viewModel - clickSignUpBtn() + binding.viewModel = signUpViewModel + observeSignUpResult() signUp() } private fun clickSignUpBtn() { binding.btSignUp.setOnClickListener() { - if (viewModel.isConditionSatisfied()) + if (signUpViewModel.isConditionSatisfied()) processSignUp() else { - val errorString = "please check for " + viewModel.getInvalidFormatField() + val errorString = "please check for " + signUpViewModel.getInvalidFormatField() makeToast(this, errorString) } } @@ -42,25 +44,7 @@ class SingUpActivity : AppCompatActivity() { val id = binding.etId.text.toString() val password = binding.etPw.text.toString() val nickname = binding.etNickName.text.toString() - - authService.signUp(RequestSignUpDto(id, nickname, password)) - .enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - processSignUp() - } else { - makeToast(this@SingUpActivity, getString(R.string.signUpFail)) - } - } - - override fun onFailure(call: Call, t: Throwable) { - makeToast(this@SingUpActivity, getString(R.string.serverError)) - } - - }) + authViewModel.signUp(id, nickname, password) } } @@ -72,6 +56,17 @@ class SingUpActivity : AppCompatActivity() { // TODO: saveSignUpData() } + private fun observeSignUpResult() { + authViewModel.signUpSuccess.observe(this) { + if( it ) { + processSignUp() + } + else { + val errorString = "please check for " + signUpViewModel.getInvalidFormatField() + makeToast(this, errorString) + } + } + } companion object { const val SIGNUPINFO = "sign up info" } diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index b8072be..9ecf69b 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -47,7 +47,7 @@ android:layout_height="wrap_content" android:hint="@string/id_hint" android:inputType="text" - android:text="@={viewModel.id}" + android:text="@={authViewModel._id}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_id" /> @@ -66,10 +66,18 @@ android:layout_height="wrap_content" android:hint="@string/pw_hint" android:inputType="textPassword" - android:text="@={viewModel.password}" + android:text="@={authViewModel._password}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_password" /> - + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b86850d..c56bb42 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -39,4 +39,7 @@ 회원가입 실패. + 로그인 버튼이 클릭 되었습니다. + 로그인 버튼을 클릭해주세요. + \ No newline at end of file From b5522ddbc1c7d5fee9a3ff758f7a17eca301b530 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 28 Nov 2023 20:15:23 +0900 Subject: [PATCH 14/16] =?UTF-8?q?[chore]=20login=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B0=94=EC=9D=B8=EB=94=A9?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=A1=B0=EA=B1=B4=ED=99=95=EC=9D=B8?= =?UTF-8?q?=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/dosopttemplate/api/AuthViewModel.kt | 37 ++++++++----------- .../presentation/signup/SignUpActivity.kt | 13 ++----- .../sopt/dosopttemplate/util/ToastMaker.kt | 16 -------- .../org/sopt/dosopttemplate/util/UtilClass.kt | 30 +++++++++++++++ app/src/main/res/layout/activity_login.xml | 3 ++ 5 files changed, 52 insertions(+), 47 deletions(-) delete mode 100644 app/src/main/java/org/sopt/dosopttemplate/util/ToastMaker.kt create mode 100644 app/src/main/java/org/sopt/dosopttemplate/util/UtilClass.kt diff --git a/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt index 1979263..793df0a 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt @@ -6,6 +6,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch import org.sopt.dosopttemplate.api.ServicePool.authService +import org.sopt.dosopttemplate.util.UtilClass.isIdConditionSatisfied +import org.sopt.dosopttemplate.util.UtilClass.isPasswordConditionSatisfied class AuthViewModel : ViewModel() { // MutableLiveData를 사용하여 login result 객체를 생성합니다. @@ -23,33 +25,16 @@ class AuthViewModel : ViewModel() { val signUpSuccess: MutableLiveData get() = _signUpSuccess val isLoginButtonClicked: MutableLiveData = MutableLiveData(false) + val loginConditionSatisfied: Boolean get() = idConditionSatisfied.value ?: false && passwordConditionSatisfied.value ?: false + val idConditionSatisfied: MutableLiveData = MutableLiveData(false) + val passwordConditionSatisfied: MutableLiveData = MutableLiveData(false) val _id: MutableLiveData = MutableLiveData() - val id: String get() = _id.value?:"" + val id: String get() = _id.value ?: "" val _password: MutableLiveData = MutableLiveData() - val password: String get() = _password.value?:"" + val password: String get() = _password.value ?: "" fun login() { -// authService.login(RequestLoginDto(id, password)) -// .enqueue(object : Callback { -// override fun onResponse( -// call: Call, -// response: Response, -// ) { -// if (response.isSuccessful) { -// _loginResult.value = response.body() -// _loginSuccess.value = true -// } else { -// // TODO: 에러 처리 로직 -// _loginSuccess.value = false -// } -// } -// -// override fun onFailure(call: Call, t: Throwable) { -// // TODO: 에러 처리 로직 -// } -// }) - viewModelScope.launch { kotlin.runCatching { authService.login(RequestLoginDto(id, password)) @@ -87,4 +72,12 @@ class AuthViewModel : ViewModel() { fun onLoginButtonClick() { isLoginButtonClicked.value = true } + + fun onIDTextChanged() { + idConditionSatisfied.value = isIdConditionSatisfied(id) + } + + fun onPasswordTextChanged() { + passwordConditionSatisfied.value = isPasswordConditionSatisfied(password) + } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt index 7da4e9e..78adc2e 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt @@ -4,15 +4,10 @@ import android.os.Bundle import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil -import org.sopt.dosopttemplate.api.RequestSignUpDto -import org.sopt.dosopttemplate.api.ServicePool.authService import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.api.AuthViewModel import org.sopt.dosopttemplate.databinding.ActivitySignupBinding -import org.sopt.dosopttemplate.util.ToastMaker.makeToast -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response +import org.sopt.dosopttemplate.util.UtilClass.makeToast class SingUpActivity : AppCompatActivity() { private lateinit var binding: ActivitySignupBinding @@ -58,15 +53,15 @@ class SingUpActivity : AppCompatActivity() { private fun observeSignUpResult() { authViewModel.signUpSuccess.observe(this) { - if( it ) { + if (it) { processSignUp() - } - else { + } else { val errorString = "please check for " + signUpViewModel.getInvalidFormatField() makeToast(this, errorString) } } } + companion object { const val SIGNUPINFO = "sign up info" } diff --git a/app/src/main/java/org/sopt/dosopttemplate/util/ToastMaker.kt b/app/src/main/java/org/sopt/dosopttemplate/util/ToastMaker.kt deleted file mode 100644 index de705ce..0000000 --- a/app/src/main/java/org/sopt/dosopttemplate/util/ToastMaker.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.sopt.dosopttemplate.util - -import android.content.Context -import android.widget.Toast -import androidx.lifecycle.MutableLiveData - -object ToastMaker { - private var toast: Toast? = null - fun makeToast(context: Context, msg: String) { - toast?.cancel() - toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT) - toast?.show() - } -} - -fun getMutableDataString(str: MutableLiveData):String = str.value.toString() \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/util/UtilClass.kt b/app/src/main/java/org/sopt/dosopttemplate/util/UtilClass.kt new file mode 100644 index 0000000..c619095 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/util/UtilClass.kt @@ -0,0 +1,30 @@ +package org.sopt.dosopttemplate.util + +import android.content.Context +import android.widget.Toast +import androidx.lifecycle.MutableLiveData + +object UtilClass { + private var toast: Toast? = null + fun makeToast(context: Context, msg: String) { + toast?.cancel() + toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT) + toast?.show() + } + + fun checkStringLengthOf(str: String?, min: Int, max: Int): Boolean = + str?.length in min..max + + fun isIdConditionSatisfied(str: String): Boolean { + val englishAndNumRegex: Regex = Regex("^(?=.*[0-9])(?=.*[a-zA-Z]).{6,10}\$") + return str.matches(englishAndNumRegex) + } + + fun isPasswordConditionSatisfied(str: String): Boolean { + val englishAndNumSpecialRegex: Regex = Regex("^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[!@#\$%^&*()-_=+]).{6,10}\$") + return str.matches(englishAndNumSpecialRegex) + } + +} + +fun getMutableDataString(str: MutableLiveData): String = str.value.toString() \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 9ecf69b..1dd5a67 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -48,6 +48,7 @@ android:hint="@string/id_hint" android:inputType="text" android:text="@={authViewModel._id}" + android:onTextChanged="@{() -> authViewModel.onIDTextChanged()}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_id" /> @@ -67,6 +68,7 @@ android:hint="@string/pw_hint" android:inputType="textPassword" android:text="@={authViewModel._password}" + android:onTextChanged="@{() -> authViewModel.onPasswordTextChanged()}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_password" /> From 5ea02c77991d2ed5c8464070e9ea50313f299ccf Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 28 Nov 2023 22:52:30 +0900 Subject: [PATCH 15/16] =?UTF-8?q?[feat]=20=EC=83=88=EB=A1=9C=EC=9A=B4=20id?= =?UTF-8?q?,=20password=20=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80,=20text?= =?UTF-8?q?=20input=20layout=20=20=ED=99=9C=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 9 +- .../sopt/dosopttemplate/api/AuthViewModel.kt | 19 ++- .../presentation/home/HomeFragment.kt | 6 +- .../presentation/login/LoginActivity.kt | 23 ++- .../presentation/signup/SignUpActivity.kt | 22 ++- .../org/sopt/dosopttemplate/util/UtilClass.kt | 2 +- app/src/main/res/layout/activity_login.xml | 115 +++++++------ app/src/main/res/layout/activity_signup.xml | 151 ++++++++++++------ app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/themes.xml | 8 + 10 files changed, 244 insertions(+), 112 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2d129e8..ff82481 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,7 +15,8 @@ android:usesCleartextTraffic="true"> + android:exported="true" + android:windowSoftInputMode="adjustResize"> @@ -24,7 +25,8 @@ + android:exported="false" + android:windowSoftInputMode="adjustResize"> + android:exported="false" + android:windowSoftInputMode="adjustResize"> diff --git a/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt index 793df0a..56b372d 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt @@ -1,6 +1,7 @@ package org.sopt.dosopttemplate.api// 보시면 알겠지만 LiveData는 lifecycle 라이브러리에 들어있습니다! // 서비스 객체를 따로 만들지 않고 바로 불러왔습니다. // 언제나 그렇듯 Call과 Callback은 Retrofit2의 것을 사용해야 합니다. okhttp 아님 주의! +import android.util.Log import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -25,14 +26,16 @@ class AuthViewModel : ViewModel() { val signUpSuccess: MutableLiveData get() = _signUpSuccess val isLoginButtonClicked: MutableLiveData = MutableLiveData(false) - val loginConditionSatisfied: Boolean get() = idConditionSatisfied.value ?: false && passwordConditionSatisfied.value ?: false val idConditionSatisfied: MutableLiveData = MutableLiveData(false) val passwordConditionSatisfied: MutableLiveData = MutableLiveData(false) + val loginConditionSatisfied: MutableLiveData = MutableLiveData(idConditionSatisfied.value?:false && passwordConditionSatisfied.value?:false) val _id: MutableLiveData = MutableLiveData() val id: String get() = _id.value ?: "" val _password: MutableLiveData = MutableLiveData() val password: String get() = _password.value ?: "" + val _nickName: MutableLiveData = MutableLiveData() + val nickName: String get() = _nickName.value ?: "" fun login() { viewModelScope.launch { @@ -51,10 +54,10 @@ class AuthViewModel : ViewModel() { } } - fun signUp(id: String, nickname: String, password: String) { + fun signUp() { viewModelScope.launch { kotlin.runCatching { - authService.signUp(RequestSignUpDto(id, nickname, password)) + authService.signUp(RequestSignUpDto(id, nickName, password)) }.onSuccess { if (it.isSuccessful) { signUpResult.value = it.body() @@ -75,9 +78,17 @@ class AuthViewModel : ViewModel() { fun onIDTextChanged() { idConditionSatisfied.value = isIdConditionSatisfied(id) + loginConditionSatisfied.value = idConditionSatisfied.value?:false && passwordConditionSatisfied.value?:false + } + fun onIDTextChangedForSignUp() { + idConditionSatisfied.value = isIdConditionSatisfied(id) + loginConditionSatisfied.value = idConditionSatisfied.value?:false && passwordConditionSatisfied.value?:false + Log.v("id", id.toString()) + Log.v("id condition", idConditionSatisfied.value.toString()) } - fun onPasswordTextChanged() { passwordConditionSatisfied.value = isPasswordConditionSatisfied(password) + loginConditionSatisfied.value = idConditionSatisfied.value?:false && passwordConditionSatisfied.value?:false + Log.v("logincondition", loginConditionSatisfied.value.toString()) } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt index 546526d..c8f99ba 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/HomeFragment.kt @@ -5,11 +5,11 @@ import android.os.Bundle import android.util.Log import android.view.View import androidx.fragment.app.viewModels +import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.api.ResponseGetFollwerDto import org.sopt.dosopttemplate.api.ServicePool -import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.databinding.FragmentHomeBinding -import org.sopt.dosopttemplate.util.ToastMaker +import org.sopt.dosopttemplate.util.UtilClass.makeToast import org.sopt.dosopttemplate.util.binding.BindingFragment import retrofit2.Call import retrofit2.Callback @@ -50,7 +50,7 @@ class HomeFragment : BindingFragment(R.layout.fragment_home override fun onFailure(call: Call, t: Throwable) { Log.v("serverError", t.toString()) - ToastMaker.makeToast(context as Activity, getString(R.string.serverError)) + makeToast(context as Activity, getString(R.string.serverError)) } }) } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt index a3106d9..d8f70f0 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginActivity.kt @@ -14,7 +14,7 @@ import org.sopt.dosopttemplate.data.SignUpInfo import org.sopt.dosopttemplate.databinding.ActivityLoginBinding import org.sopt.dosopttemplate.presentation.home.HomeActivity import org.sopt.dosopttemplate.presentation.signup.SingUpActivity -import org.sopt.dosopttemplate.util.ToastMaker.makeToast +import org.sopt.dosopttemplate.util.UtilClass.makeToast import org.sopt.dosopttemplate.util.getParcelable import retrofit2.Response @@ -34,6 +34,7 @@ class LoginActivity : AppCompatActivity() { login() clickSignUpBtn() observeLoginResult() + observeLoginText() } private fun getSignUpInfo() { @@ -118,5 +119,25 @@ class LoginActivity : AppCompatActivity() { } } + private fun observeLoginText() { + authViewModel.idConditionSatisfied.observe(this) { + if (it == false) { + binding.tilId.error = "영문, 숫자가 포함된 6~10글자를 입력해주세요." + } else { + binding.tilId.error = null + } + + } + authViewModel.passwordConditionSatisfied.observe(this) { + if (it == false) { + binding.tilPassword.error = "영문, 숫자, 특수문자가 포함된 6~12글자를 입력해주세요." + } else { + binding.tilPassword.error = null + } + + } + } + + } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt index 78adc2e..f40906b 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/signup/SignUpActivity.kt @@ -19,8 +19,9 @@ class SingUpActivity : AppCompatActivity() { binding = DataBindingUtil.setContentView(this, R.layout.activity_signup) binding.lifecycleOwner = this binding.viewModel = signUpViewModel + binding.authViewModel = authViewModel observeSignUpResult() - signUp() + observeLoginCondition() } private fun clickSignUpBtn() { @@ -39,8 +40,6 @@ class SingUpActivity : AppCompatActivity() { val id = binding.etId.text.toString() val password = binding.etPw.text.toString() val nickname = binding.etNickName.text.toString() - authViewModel.signUp(id, nickname, password) - } } @@ -62,6 +61,23 @@ class SingUpActivity : AppCompatActivity() { } } + private fun observeLoginCondition() { + authViewModel.idConditionSatisfied.observe(this) { + if (it) { + binding.tilId.error = null + } else { + binding.tilId.error = "영문, 숫자가 포함된 6~10글자를 입력해주세요." + } + } + authViewModel.passwordConditionSatisfied.observe(this) { + if (it) { + binding.tilPassword.error = null + } else { + binding.tilPassword.error = "영문, 숫자, 특수문자가 포함된 6~12글자를 입력해주세요." + } + } + } + companion object { const val SIGNUPINFO = "sign up info" } diff --git a/app/src/main/java/org/sopt/dosopttemplate/util/UtilClass.kt b/app/src/main/java/org/sopt/dosopttemplate/util/UtilClass.kt index c619095..3e70a53 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/util/UtilClass.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/util/UtilClass.kt @@ -21,7 +21,7 @@ object UtilClass { } fun isPasswordConditionSatisfied(str: String): Boolean { - val englishAndNumSpecialRegex: Regex = Regex("^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[!@#\$%^&*()-_=+]).{6,10}\$") + val englishAndNumSpecialRegex: Regex = Regex("^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[!@#\$%^&*()-_=+]).{6,12}\$") return str.matches(englishAndNumSpecialRegex) } diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 1dd5a67..28689c6 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -10,11 +10,12 @@ + - + - + app:layout_constraintTop_toBottomOf="@id/tv_id" + > + + + + app:layout_constraintTop_toBottomOf="@id/til_id" /> + + + - - + android:text="@={authViewModel._password}"/> + - - -