diff --git a/app/build.gradle b/app/build.gradle index edafe73..5c39981 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 @@ -14,12 +16,14 @@ 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 dataBinding true + buildConfig true } buildTypes { release { @@ -34,7 +38,6 @@ android { kotlinOptions { jvmTarget = '11' } - } dependencies { @@ -49,4 +52,12 @@ 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' + implementation(platform("com.squareup.okhttp3:okhttp-bom:4.10.0")) + 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 29f2120..0d3f3f1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + + tools:targetApi="31" + android:usesCleartextTraffic="true"> + android:exported="true"> + + + + + android:exported="false"> + android:exported="true"> android:exported="true" android:windowSoftInputMode="adjustResize" diff --git a/app/src/main/java/org/sopt/dosopttemplate/api/APIFactory.kt b/app/src/main/java/org/sopt/dosopttemplate/api/APIFactory.kt new file mode 100644 index 0000000..364d248 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/api/APIFactory.kt @@ -0,0 +1,49 @@ +package org.sopt.dosopttemplate.api + +import android.util.Log +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import kotlinx.serialization.json.Json +import okhttp3.Interceptor +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.sopt.dosopttemplate.BuildConfig +import retrofit2.Retrofit + +object ApiFactory { + lateinit var url: String + private fun getLogOkHttpClient(): Interceptor { + val loggingInterceptor = HttpLoggingInterceptor { message -> + 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(url) + .client(okHttpClient) + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .build() + } + + inline fun create(url: String): T { + this.url = url + return retrofit.create(T::class.java) + } +} + +object ServicePool { + 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/AuthService.kt b/app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt new file mode 100644 index 0000000..029cd78 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt @@ -0,0 +1,19 @@ +package org.sopt.dosopttemplate.api + +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 + + @POST("api/v1/members") + fun signUp( + @Body request: RequestSignUpDto, + ): Call +} + + 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 new file mode 100644 index 0000000..660867d --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/api/RequestLoginDto.kt @@ -0,0 +1,12 @@ +package org.sopt.dosopttemplate.api + +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/api/RequestSignUpDto.kt b/app/src/main/java/org/sopt/dosopttemplate/api/RequestSignUpDto.kt new file mode 100644 index 0000000..d924cd1 --- /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/api/ResponseGetFollowerDto.kt b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollowerDto.kt new file mode 100644 index 0000000..8f5af3c --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseGetFollowerDto.kt @@ -0,0 +1,42 @@ +package org.sopt.dosopttemplate.api + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseGetFollwerDto( + @SerialName("page") + val page: Int, + @SerialName("per_page") + val perPage: Int, + @SerialName("total") + val total: Int, + @SerialName("total_pages") + val totalPages: Int, + @SerialName("data") + val data: List, + @SerialName("support") + val support: ResponseSupport +) + +@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, +) diff --git a/app/src/main/java/org/sopt/dosopttemplate/api/ResponseLoginDto.kt b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseLoginDto.kt new file mode 100644 index 0000000..d5d31a2 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/api/ResponseLoginDto.kt @@ -0,0 +1,14 @@ +package org.sopt.dosopttemplate.api + +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, +) 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..167f8ff --- /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/data/Music.kt b/app/src/main/java/org/sopt/dosopttemplate/data/Music.kt index 17793a3..89bb34a 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/data/Music.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/data/Music.kt @@ -2,31 +2,9 @@ package org.sopt.dosopttemplate.data import android.os.Parcel import android.os.Parcelable +import kotlinx.parcelize.Parcelize +@Parcelize data class Music(val title: String, val artist: String) : Parcelable { val string: String = "🎧$title - $artist" - - constructor(parcel: Parcel) : this( - parcel.readString()!!, - parcel.readString()!! - ) - - override fun writeToParcel(parcel: Parcel, flags: Int) { - parcel.writeString(title) - parcel.writeString(artist) - } - - override fun describeContents(): Int { - return 0 - } - - companion object CREATOR : Parcelable.Creator { - 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 1a79e63..f7dd199 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/data/Profile.kt @@ -5,80 +5,99 @@ 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 profileImage: Int?, + val id: String?, + var nickname: String, val type: Int, - var MBTI: String?, - var intro: String?, - val id: 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( - @DrawableRes profileImage: Int, - name: String, - musicTitle: String, - musicArtist: String, - type: Int, - ) : this( - profileImage, - name, - musicTitle, - musicArtist, - type, - null, - null, - null - ) constructor( - name: String, + nickname: String, type: Int, - MBTI: String, + mbti: String, intro: String ) : this( - R.drawable.img_noritake, - name, - null, + R.drawable.img_monkey, null, + nickname, type, - MBTI, - intro, - null + null, + subInfo(null, mbti, intro) ) constructor( profileImage: Int, - name: String, + id: String, + nickname: String, musicTitle: String, musicArtist: String, type: Int, - MBTI: String, + mbti: String, + intro: String, ) : this( profileImage, - name, - musicTitle, - musicArtist, + id, + nickname, type, - MBTI, null, - null + subInfo(Music(musicTitle, musicArtist), mbti, intro) ) - fun isContainMusic(): Boolean = (music != null) - fun getMusic(): String = if (isContainMusic()) music!!.string else "no music" + constructor( + nickname: String, + email: String, + avatar: String, + idInt: Int + ) : this( + null, + null, + nickname, + FRIEND, + ReqresData(email, avatar, idInt), + subInfo(null) + ) - 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 144ebbe..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 @@ -2,13 +2,17 @@ package org.sopt.dosopttemplate.presentation.doandroid import android.os.Bundle import android.view.View +import androidx.fragment.app.viewModels import org.sopt.dosopttemplate.R import org.sopt.dosopttemplate.databinding.FragmentDoandroidBinding import org.sopt.dosopttemplate.util.binding.BindingFragment class DOAndroidFragment : BindingFragment(R.layout.fragment_doandroid) { + val viewModel by viewModels() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) } + } \ No newline at end of file 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/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 8c712e4..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 @@ -1,14 +1,21 @@ 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) - tvName.text = friendData.name - tvMusic.text = friendData.music?.string + if (friendData.profileImage != null) + ivProfile.setImageResource(friendData.profileImage) + else { + Glide.with(binding.root) + .load(friendData.reqresData?.avatar) + .into(ivProfile) + } + 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..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 @@ -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,35 @@ 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()) + ToastMaker.makeToast(context as Activity, 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..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 @@ -2,104 +2,54 @@ package org.sopt.dosopttemplate.presentation.home import androidx.lifecycle.ViewModel import org.sopt.dosopttemplate.R +import org.sopt.dosopttemplate.api.ResponseData import org.sopt.dosopttemplate.data.Profile 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 { + 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/home/MyProfileViewHolder.kt b/app/src/main/java/org/sopt/dosopttemplate/presentation/home/MyProfileViewHolder.kt index ffbba2d..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 @@ -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) - binding.tvName.text = myProfileData.name + if (myProfileData.profileImage != null) + binding.ivProfile.setImageResource(myProfileData.profileImage) + binding.tvName.text = myProfileData.nickname } } \ 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..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 @@ -8,26 +8,31 @@ import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil 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.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.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 val authViewModel 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() + observeLoginResult() } private fun getSignUpInfo() { @@ -38,27 +43,52 @@ 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() { + + binding.btLogin.setOnClickListener { + val id = binding.etId.text.toString() + val password = binding.etPassword.text.toString() + + 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)) +// } +// }) } } + private fun processLogin(response: Response) { + val data: ResponseLoginDto = + requireNotNull(response.body()) { "Response body should not be null" } + 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) } @@ -70,5 +100,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/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) { 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 cdc1b9f..90c5d94 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 import org.sopt.dosopttemplate.util.hideKeyboard class SingUpActivity : AppCompatActivity() { @@ -21,6 +25,7 @@ class SingUpActivity : AppCompatActivity() { binding.viewModel = viewModel hideKeyboardOnClickScreen() clickSignUpBtn() + signUp() } private fun hideKeyboardOnClickScreen() { @@ -32,7 +37,7 @@ class SingUpActivity : AppCompatActivity() { private fun clickSignUpBtn() { binding.btSignUp.setOnClickListener() { if (viewModel.isConditionSatisfied()) - signUp() + processSignUp() else { val errorString = "please check for " + viewModel.getInvalidFormatField() makeToast(this, errorString) @@ -41,18 +46,38 @@ class SingUpActivity : AppCompatActivity() { } private fun signUp() { - makeToast(this, "회원가입 완료!") - sendDataToLoginActivity() -// TODO: saveSignUpData() - } + 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 sendDataToLoginActivity() { - with(intent) { - val signUpData = viewModel.createSignUpInfo() - putExtra(SIGNUPINFO, signUpData) } + } + + private fun processSignUp() { + makeToast(this, "회원가입 완료!") setResult(RESULT_OK, intent) finish() +// TODO: saveSignUpData() } companion object { @@ -61,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 c9654d4..1a75eb0 100644 --- a/app/src/main/java/org/sopt/dosopttemplate/util/IntentExt.kt +++ b/app/src/main/java/org/sopt/dosopttemplate/util/IntentExt.kt @@ -18,13 +18,6 @@ fun Intent.getParcelable(name: String, clazz: Class): T? { 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()) - fun hideKeyboard(activity:Activity) { val imm: InputMethodManager = activity.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager 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/drawable/img_noritake.jpg b/app/src/main/res/drawable/img_noritake.jpg deleted file mode 100644 index e403b7d..0000000 Binary files a/app/src/main/res/drawable/img_noritake.jpg and /dev/null differ 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"> - - + + + + - + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6a7a534..b86850d 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