diff --git a/app/build.gradle b/app/build.gradle index 8cf32f9..028015d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,6 +40,7 @@ android { } buildFeatures { viewBinding = true + dataBinding = true //사용할 객체 가져오기 (base_url 감추는 작업) buildConfig = true @@ -81,5 +82,6 @@ dependencies { //이미지 로드 implementation 'io.coil-kt:coil:2.4.0' - + //코루틴 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/dataclass/User.kt b/app/src/main/java/org/sopt/dosopttemplate/data/dataclass/User.kt index 0ad16ee..08803c9 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/data/dataclass/User.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/data/dataclass/User.kt @@ -4,10 +4,10 @@ import org.sopt.dosopttemplate.R data class User( val profileImage: Int= R.drawable.pr_image, - var nick: String="", - val self_description: String="", - var id: String="", - var pw: String="", - var mbti: String="", + var nick: String, + val self_description: String, + var id: String, + var pw: String, + var mbti: String, ) diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/dataclass/UserInfo.kt b/app/src/main/java/org/sopt/dosopttemplate/data/dataclass/UserInfo.kt new file mode 100644 index 0000000..b877580 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/data/dataclass/UserInfo.kt @@ -0,0 +1,16 @@ +package org.sopt.dosopttemplate.data.dataclass + +object UserInfo { + var userInfoList = User( + nick = "", + self_description = "", + id = "", + pw = "", + mbti = "", + ) + + fun toUser(inId: String, inPw: String) { + userInfoList.id = inId + userInfoList.pw = inPw + } +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/dosopttemplate/data/dto/request/RequestLoginDto.kt b/app/src/main/java/org/sopt/dosopttemplate/data/dto/request/RequestLoginDto.kt index e712869..b71464b 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/data/dto/request/RequestLoginDto.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/data/dto/request/RequestLoginDto.kt @@ -7,7 +7,6 @@ import kotlinx.serialization.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/module/AuthService.kt b/app/src/main/java/org/sopt/dosopttemplate/module/AuthService.kt index 5e52203..340678a 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/module/AuthService.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/module/AuthService.kt @@ -6,6 +6,7 @@ import org.sopt.dosopttemplate.data.dto.request.RequestUserDataDto import org.sopt.dosopttemplate.data.dto.response.ResponseLoginDto import retrofit2.Call import org.sopt.dosopttemplate.data.dto.response.ResponseUserDataDto +import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST @@ -14,17 +15,17 @@ import retrofit2.http.Path interface AuthService { @POST("api/v1/members/sign-in") - fun postLogin( + suspend fun postLogin( @Body request: RequestLoginDto, - ): Call + ): Response @POST("api/v1/members") - fun signUp( + suspend fun postSignUp( @Body request: RequestSignUpDto, - ): Call + ): Response @GET("api/v1/members/{memberId}") fun getUserInfo( @Path("memberId") memberId: Int - ): Call + ): Response } \ 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 d89cf71..d96d31c 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 @@ -4,27 +4,26 @@ package org.sopt.dosopttemplate.presentation.login import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch +import org.sopt.dosopttemplate.databinding.ActivityLoginBinding import org.sopt.dosopttemplate.presentation.mainhome.MainHomeActivity -import org.sopt.dosopttemplate.module.ServicePool.authService +import org.sopt.dosopttemplate.presentation.mainhome.MainHomeViewModel import org.sopt.dosopttemplate.presentation.signUp.SignUpActivity -import org.sopt.dosopttemplate.databinding.ActivityLoginBinding -import org.sopt.dosopttemplate.data.dto.request.RequestLoginDto -import org.sopt.dosopttemplate.data.dto.response.ResponseLoginDto import org.sopt.dosopttemplate.utils.toast -import org.sopt.dosopttemplate.utils.snackbar -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response + class LoginActivity : AppCompatActivity() { private lateinit var binding: ActivityLoginBinding + private lateinit var loginViewModel: LoginViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityLoginBinding.inflate(layoutInflater) setContentView(binding.root) - + loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java) initLoginClickListener() // val receivedUserInfoList = intent.getStringArrayListExtra("userInfoList")!! @@ -63,9 +62,7 @@ class LoginActivity : AppCompatActivity() { //회원가입 페이지로 이동 val intent = Intent(this, SignUpActivity::class.java) startActivity(intent) - } - } private fun initLoginClickListener() { @@ -73,12 +70,35 @@ class LoginActivity : AppCompatActivity() { val id = binding.etLoginId.text.toString() val password = binding.etLoginPw.text.toString() binding.btLogin.setOnClickListener { - checkLoginAvailableFromServer(id, password) + loginViewModel.checkLoginFromServer(id, password) + checkLoginAvailableFromServer() } } } - private fun checkLoginAvailableFromServer(id: String, password: String) { + private fun checkLoginAvailableFromServer() { + lifecycleScope.launch { + loginViewModel.loginState.collect { loginState -> + when (loginState) { + is LoginState.Success -> { + toast("로그인 성공") + val intent = Intent(this@LoginActivity, MainHomeActivity::class.java) + startActivity(intent) + } + + is LoginState.Error -> { + toast("로그인 실패") + } + + is LoginState.Loading -> {} + + else -> {} + } + } + } + } + + /* authService.postLogin(RequestLoginDto(id, password)) .enqueue(object : Callback { override fun onResponse( @@ -88,7 +108,7 @@ class LoginActivity : AppCompatActivity() { if (response.isSuccessful) { val data: ResponseLoginDto = requireNotNull(response.body()!!) val userId: Int = data.id - toast("로그인 성공, 유저의 ID는 $userId 입니다") + toast("로그인 성공") val intent = Intent(this@LoginActivity, MainHomeActivity::class.java) intent.putExtra("id", userId) startActivity(intent) @@ -101,7 +121,7 @@ class LoginActivity : AppCompatActivity() { toast("서버 에러 발생") } }) - } +*/ } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginState.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginState.kt new file mode 100644 index 0000000..ca86137 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginState.kt @@ -0,0 +1,10 @@ +package org.sopt.dosopttemplate.presentation.login + +import org.sopt.dosopttemplate.data.dto.response.ResponseLoginDto + +sealed class LoginState { + data class Success(val userId: ResponseLoginDto) : LoginState() + object Loading : LoginState() + object Error : LoginState() + object Unstarted : LoginState() +} \ 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 new file mode 100644 index 0000000..ee82924 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/login/LoginViewModel.kt @@ -0,0 +1,75 @@ +package org.sopt.dosopttemplate.presentation.login + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import org.sopt.dosopttemplate.data.dataclass.UserInfo +import org.sopt.dosopttemplate.data.dto.request.RequestLoginDto +import org.sopt.dosopttemplate.module.ApiFactory +import org.sopt.dosopttemplate.module.AuthService + +class LoginViewModel() : ViewModel() { + private val _loginSuccess: MutableLiveData = MutableLiveData() + var loginSuccess: LiveData = _loginSuccess + + private val _loginState = MutableStateFlow(LoginState.Unstarted) + val loginState: StateFlow = _loginState.asStateFlow() + + + fun checkLoginFromServer(id: String, password: String) { + viewModelScope.launch { + val loginService = ApiFactory.create() + _loginState.value = LoginState.Loading + kotlin.runCatching { + loginService.postLogin(RequestLoginDto(id, password)) + }.onSuccess { + val body = it.body() + if (it.body() != null) { + _loginState.value = LoginState.Success(requireNotNull(body)) + UserInfo.toUser(id, password) + + } else { + _loginState.value = LoginState.Error + } + }.onFailure { + _loginState.value = LoginState.Error + } + +///* +// ServicePool.authService.postLogin(RequestLoginDto(id, password)) +// .enqueue(object : Callback { +// override fun onResponse( +// call: Call, +// response: Response, +// ) { +// if (response.isSuccessful) { +// val data: ResponseLoginDto = requireNotNull(response.body()!!) +// +// +// val userId: Int = data.id +// loginSuccess.value = true +// toast("로그인 성공") +// val intent = Intent(context, MainHomeActivity::class.java) +// intent.putExtra("id", userId) +// +// +// startActivity(intent) +// } else { +// toast("아이디와 패스워드가 일치 하지 않습니다.") +// } +// } +// +// override fun onFailure(call: Call, t: Throwable) { +// toast("서버 에러 발생") +// } +// }) +//*/ + + } + } +} diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/mainhome/MainHomeActivity.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/mainhome/MainHomeActivity.kt index 61cf081..15e0e09 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/mainhome/MainHomeActivity.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/mainhome/MainHomeActivity.kt @@ -48,8 +48,6 @@ class MainHomeActivity : AppCompatActivity() { private fun clickBottomNavigation() { var userId = intent.getIntExtra("id", -1) - - mainHomeViewModel.userId=userId binding.bnvHome.setOnItemSelectedListener { mainHomeViewModel.clickBottomNavigation(it.itemId) } diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/mainhome/MainHomeViewModel.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/mainhome/MainHomeViewModel.kt index e807b0e..729fea5 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/mainhome/MainHomeViewModel.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/mainhome/MainHomeViewModel.kt @@ -4,9 +4,12 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow import org.sopt.dosopttemplate.MyApplication import org.sopt.dosopttemplate.data.dataclass.Friend import org.sopt.dosopttemplate.R +import org.sopt.dosopttemplate.data.dataclass.User +import org.sopt.dosopttemplate.data.dataclass.UserInfo import org.sopt.dosopttemplate.data.dto.request.RequestUserDataDto import org.sopt.dosopttemplate.data.dto.response.ResponseUserDataDto import org.sopt.dosopttemplate.module.ServicePool.authService @@ -19,10 +22,13 @@ import retrofit2.Response class MainHomeViewModel : ViewModel() { private val _navigateTo = MutableLiveData() - val navigateTo: LiveData - get() = _navigateTo + val navigateTo: LiveData get() = _navigateTo + + private var _userId: MutableLiveData = MutableLiveData() + val userId: LiveData = _userId + val userPw: MutableLiveData = MutableLiveData() + - var userId: Int = 0 fun clickBottomNavigation(itemId: Int): Boolean { when (itemId) { @@ -37,7 +43,7 @@ class MainHomeViewModel : ViewModel() { } R.id.menu_mypage -> { - myPageUserInfo(userId) + myPageUserInfo() _navigateTo.value = MyPageFragment() return true } @@ -52,29 +58,9 @@ class MainHomeViewModel : ViewModel() { } - private fun myPageUserInfo(userId: Int) { - authService.getUserInfo(userId) - .enqueue(object : retrofit2.Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (response.isSuccessful) { - val data: ResponseUserDataDto = requireNotNull(response.body()) - val userNickname = data.nickname - val userUsername = data.username - - //sharedpreference로 넘기기 - MyApplication.prefs.setString("nick", userNickname) - MyApplication.prefs.setString("username", userUsername) - MyApplication.prefs.setString("id", userId.toString()) - } - } - - override fun onFailure(call: Call, t: Throwable) { - _errorMsg.value = "서버 에러 발생" - } - }) + private fun myPageUserInfo() { + _userId.value = UserInfo.userInfoList.id + userPw.value = UserInfo.userInfoList.pw } private var _errorMsg = MutableLiveData() diff --git a/app/src/main/java/org/sopt/dosopttemplate/presentation/mypage/MyPageFragment.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/mypage/MyPageFragment.kt index 90e8f5b..dd57280 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/presentation/mypage/MyPageFragment.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/mypage/MyPageFragment.kt @@ -6,12 +6,15 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import org.sopt.dosopttemplate.MyApplication import org.sopt.dosopttemplate.databinding.FragmentMypageBinding +import org.sopt.dosopttemplate.presentation.mainhome.MainHomeViewModel class MyPageFragment : Fragment() { private var _binding: FragmentMypageBinding? = null - private val binding: FragmentMypageBinding get() = requireNotNull(_binding!!) + private val binding: FragmentMypageBinding get() = requireNotNull(_binding) + private val viewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, @@ -38,6 +41,7 @@ class MyPageFragment : Fragment() { } override fun onDestroyView() { + _binding = null super.onDestroyView() } } \ 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 62963e8..c5a96d4 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,22 +4,18 @@ import android.content.Intent import android.os.Bundle import android.text.Editable import android.text.TextWatcher +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity - import org.sopt.dosopttemplate.R -import org.sopt.dosopttemplate.module.ServicePool.authService import org.sopt.dosopttemplate.databinding.ActivitySignupBinding import org.sopt.dosopttemplate.presentation.login.LoginActivity -import org.sopt.dosopttemplate.data.dto.request.RequestSignUpDto -import org.sopt.dosopttemplate.data.dto.response.ResponseSignUpDto import org.sopt.dosopttemplate.utils.toast -import retrofit2.Call -import retrofit2.Response import java.util.regex.Pattern class SignUpActivity : AppCompatActivity() { private lateinit var binding: ActivitySignupBinding + private val signUpViewModel by viewModels() //false 일 때 가입 불가 //true 일 때 가입 가능 @@ -42,13 +38,12 @@ class SignUpActivity : AppCompatActivity() { btSignupButton.setOnClickListener { //가입 조건 확인 if (checkCondition() && signUpIdAvailable && signUpPwAvailable) { - signUp() - + trySignUp() + observeSignUpResult() //토스트 띄우기 val intent = Intent(this@SignUpActivity, LoginActivity::class.java) - toast("회원가입 성공") // 도전과제 꼭 할거라서 남겨놨습니다! /* // //유저정보 -> 리스트로 구성 // val userInfoList = UserInfoToListString() @@ -56,7 +51,6 @@ class SignUpActivity : AppCompatActivity() { // sendUserInfo(intent, userInfoList) // */ //액티비티 이동 - startActivity(intent) } else { toast("모든 정보를 입력해야합니다.") @@ -231,30 +225,24 @@ class SignUpActivity : AppCompatActivity() { // return userInfoList // } - - private fun signUp() = with(binding) { - val id = etSignupId.text.toString(); - val pw = etSignupPw.text.toString() - val nickname = etSignupNickname.text.toString() - - btSignupButton.setOnClickListener { - authService.signUp(RequestSignUpDto(id, pw, nickname)) - .enqueue(object : retrofit2.Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (response.isSuccessful) { - toast("회원가입 성공") - } - } - - override fun onFailure(call: Call, t: Throwable) { - toast("서버 에러 발생") - } - }) + fun trySignUp() { + with(binding) { + val id = etSignupId.text.toString(); + val pw = etSignupPw.text.toString() + val nickname = etSignupNickname.text.toString() + signUpViewModel.signUp(id, pw, nickname) } } + fun observeSignUpResult() { + signUpViewModel.signUpSuccess.observe(this) { + if (it) { + toast("회원가입 성공!!") + startActivity(intent) + } else { + toast("가입 실패") + } + } + } } 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 new file mode 100644 index 0000000..d74ce7b --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/presentation/signUp/SignUpViewModel.kt @@ -0,0 +1,32 @@ +package org.sopt.dosopttemplate.presentation.signUp + +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch +import org.sopt.dosopttemplate.data.dto.request.RequestSignUpDto +import org.sopt.dosopttemplate.module.ApiFactory +import org.sopt.dosopttemplate.module.AuthService + +class SignUpViewModel : ViewModel() { + private var _signUpSuccess: MutableLiveData = MutableLiveData() + var signUpSuccess: LiveData = _signUpSuccess + + fun signUp(id: String, pw: String, nickname: String) { + viewModelScope.launch { + val signUpService = ApiFactory.create() + kotlin.runCatching { + signUpService.postSignUp(RequestSignUpDto(id, pw, nickname)) + }.onSuccess { + _signUpSuccess.value = true + Log.e("서버", _signUpSuccess.value.toString()) + }.onFailure { + Log.e("서버", "가입 불가") + + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_gray_fill_rect.xml b/app/src/main/res/drawable/shape_gray_fill_rect.xml index 5613b99..a11be80 100644 --- a/app/src/main/res/drawable/shape_gray_fill_rect.xml +++ b/app/src/main/res/drawable/shape_gray_fill_rect.xml @@ -1,12 +1,7 @@ - - - - - - + + + + + - + - + - + + + + - - - - - + android:textStyle="bold" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - + - - + + - - + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_follower.xml b/app/src/main/res/layout/item_follower.xml index 14078c8..c34fec2 100644 --- a/app/src/main/res/layout/item_follower.xml +++ b/app/src/main/res/layout/item_follower.xml @@ -32,13 +32,11 @@ android:id="@+id/home_follower_tv_email" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginEnd="264dp" android:layout_marginStart="20dp" android:maxLines="1" android:text="이메일:" android:textSize="13sp" app:layout_constraintBottom_toBottomOf="@id/home_follower_iv_profile" - app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/home_follower_iv_profile" app:layout_constraintStart_toEndOf="@id/home_follower_iv_profile" app:layout_constraintVertical_bias="1.0" />