diff --git a/app/build.gradle b/app/build.gradle index edafe73..4e88662 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,19 +38,26 @@ android { kotlinOptions { jvmTarget = '11' } - } 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' + 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 8be4069..ff82481 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:windowSoftInputMode="adjustResize"> + + + + + android:exported="false" + android:windowSoftInputMode="adjustResize"> + - - - - + android:exported="false"> + android:exported="false" + 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..02e38e5 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/api/AuthService.kt @@ -0,0 +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") + suspend fun login( + @Body request: RequestLoginDto, + ): Response + + @POST("api/v1/members") + suspend fun signUp( + @Body request: RequestSignUpDto, + ): 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 new file mode 100644 index 0000000..e180196 --- /dev/null +++ b/app/src/main/java/org/sopt/dosopttemplate/api/AuthViewModel.kt @@ -0,0 +1,89 @@ +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 +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 객체를 생성합니다. + + 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) + 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 { + kotlin.runCatching { + authService.login(RequestLoginDto(id, password)) + }.onSuccess { + if (it.isSuccessful) { + loginResult.value = it.body() + loginSuccess.value = true + } else { + loginSuccess.value = false + } + }.onFailure { + // 에러 처리 + } + } + } + + fun signUp() { + 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 + } + + fun onIDTextChanged() { + idConditionSatisfied.value = isIdConditionSatisfied(id) + loginConditionSatisfied.value = idConditionSatisfied.value?:false && passwordConditionSatisfied.value?:false + } + + 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/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..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 @@ -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.R +import org.sopt.dosopttemplate.api.ResponseGetFollwerDto +import org.sopt.dosopttemplate.api.ServicePool import org.sopt.dosopttemplate.databinding.FragmentHomeBinding +import org.sopt.dosopttemplate.util.UtilClass.makeToast 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()) + 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..8fbb0af 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 @@ -2,73 +2,94 @@ package org.sopt.dosopttemplate.presentation.login import android.content.Intent import android.os.Bundle +import android.util.Log import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts 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.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 org.sopt.dosopttemplate.util.UtilClass.makeToast class LoginActivity : AppCompatActivity() { - private lateinit var binding: ActivityMainBinding - private lateinit var signUpInfo: SignUpInfo + private lateinit var binding: ActivityLoginBinding 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() + binding.authViewModel = authViewModel clickSignUpBtn() + observeLoginResult() + observeLoginText() } - private fun getSignUpInfo() { + private fun clickSignUpBtn() { resultLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> if (result.resultCode == RESULT_OK) { - signUpInfo = - result.data?.getParcelable(LogInViewModel.SIGNUPINFO, SignUpInfo::class.java) - ?: return@registerForActivityResult - } } + binding.btSignUp.setOnClickListener() { + val intent = Intent(this, SingUpActivity::class.java) + resultLauncher.launch(intent) + } } - private fun clickLoginBtn() { - binding.btLogin.setOnClickListener() { -// Log.v("signUp Info", signUpInfo.id) - if (viewModel.isLoginAuthorized(signUpInfo)) { - makeToast(this, "로그인 성공!") + private fun observeLoginResult() { + authViewModel.loginSuccess.observe(this) { + if (it) { + makeToast( + this@LoginActivity, "로그인 성공" + ) goToMainPage() - } else - makeToast(this, "ID 또는 비밀번호가 일치하지 않습니다.") + } else { + makeToast( + this@LoginActivity, "로그인 실패" + ) + } } } 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) } + private fun observeLoginText() { + observeId() + obsercePassword() + } + + private fun obsercePassword() { + authViewModel.passwordConditionSatisfied.observe(this) { + if (it == false && authViewModel.password.isNotBlank()) { + binding.tilPassword.error = "영문, 숫자, 특수문자가 포함된 6~12글자를 입력해주세요." + } else { + binding.tilPassword.error = null + } - private fun clickSignUpBtn() { - binding.btSignUp.setOnClickListener() { - val intent = Intent(this, SingUpActivity::class.java) - resultLauncher.launch(intent) } } + private fun observeId() { + authViewModel.idConditionSatisfied.observe(this) { + if (it == false && authViewModel.id.isNotBlank()) { + binding.tilId.error = "영문, 숫자가 포함된 6~10글자를 입력해주세요." + } else { + binding.tilId.error = null + } + + } + } } 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 3ceb130..59f6b45 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,56 +1,62 @@ 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.R +import org.sopt.dosopttemplate.api.AuthViewModel import org.sopt.dosopttemplate.databinding.ActivitySignupBinding -import org.sopt.dosopttemplate.util.ToastMaker.makeToast +import org.sopt.dosopttemplate.util.UtilClass.makeToast class SingUpActivity : AppCompatActivity() { private lateinit var binding: ActivitySignupBinding - private val viewModel by viewModels() + private val authViewModel by viewModels() + private val signUpViewModel 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.signUpViewModel = signUpViewModel + binding.authViewModel = authViewModel + observeSignUpResult() + observeLoginCondition() } - private fun clickSignUpBtn() { - binding.btSignUp.setOnClickListener() { - if (viewModel.isConditionSatisfied()) - signUp() - else { - val errorString = "please check for " + viewModel.getInvalidFormatField() - makeToast(this, errorString) - } - } - } - - private fun signUp() { - makeToast(this, "회원가입 완료!") - sendDataToLoginActivity() -// TODO: saveSignUpData() + private fun processSignUp() { + makeToast(this, getString(R.string.signUpSuccess)) + setResult(RESULT_OK, intent) + finish() } - private fun sendDataToLoginActivity() { - with(intent) { - val signUpData = viewModel.createSignUpInfo() - putExtra(SIGNUPINFO, signUpData) + private fun observeSignUpResult() { + authViewModel.signUpSuccess.observe(this) { + if (it) { + processSignUp() + } else { + makeToast(this, getString(R.string.signUpFail)) + } } - setResult(RESULT_OK, intent) - finish() } - companion object { - const val SIGNUPINFO = "sign up info" + private fun observeLoginCondition() { + authViewModel.idConditionSatisfied.observe(this) { + if(it == false && authViewModel.id.isNotBlank()){ + binding.tilId.error = "영문, 숫자가 포함된 6~10글자를 입력해주세요." + } + else { + binding.tilId.error = null + } + } + authViewModel.passwordConditionSatisfied.observe(this) { + if (it == false && authViewModel.id.isNotBlank()) { + binding.tilPassword.error = "영문, 숫자, 특수문자가 포함된 6~12글자를 입력해주세요." + } else { + binding.tilPassword.error = null + } + } } } - 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..c076c5e 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 @@ -1,74 +1,14 @@ package org.sopt.dosopttemplate.presentation.signup -import android.util.Log import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import org.sopt.dosopttemplate.data.Profile -import org.sopt.dosopttemplate.data.SignUpInfo class SignUpViewModel : ViewModel() { - lateinit var signUpInfo: SignUpInfo lateinit var profile: Profile - val id: MutableLiveData = MutableLiveData() - val password: MutableLiveData = MutableLiveData() - 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() - && isNickNameFormatValid() - && isMbtiFormatValid() - && isIntroFormatValid() - && isMusicFormatValid() - } - - private fun isIDFormatValid(): Boolean = checkStringLengthOf(id.value, 6, 10) - private fun isPasswordFormatValid(): Boolean = checkStringLengthOf(password.value, 8, 12) - private fun isNickNameFormatValid(): Boolean = - checkStringLengthOf(nickName.value, 1, MAX) && isNotEmpty(nickName.value) - - private fun isMbtiFormatValid(): Boolean = - checkStringLengthOf(mbti.value, 4, 4) && isAlphabet(mbti.value.toString()) - - private fun isIntroFormatValid(): Boolean = checkStringLengthOf(intro.value, 1, MAX) - - private fun isMusicFormatValid(): Boolean = checkStringLengthOf(music.value, 1, MAX) - - private fun checkStringLengthOf(str: String?, min: Int, max: Int): Boolean = - str?.length in min..max - - private fun isNotEmpty(str: String?): Boolean = str != " " - private fun isAlphabet(string: String): Boolean { - var lengthOfAlphabet = string.filter { it in 'A'..'Z' || it in 'a'..'z' }.length - return (lengthOfAlphabet == string.length) - } - - fun createSignUpInfo(): SignUpInfo { - profile = Profile(toString(nickName), ME, toString(mbti), toString(intro)) - signUpInfo = SignUpInfo(toString(password), toString(id), profile) - Log.v("test", signUpInfo.password) - return signUpInfo - } - - private fun toString(str: MutableLiveData): String = str.value.toString() - - companion object { - const val ME = 1 - const val FRIEND = 0 - } - - fun getInvalidFormatField(): String { - if (!isIDFormatValid()) return "ID" - if (!isPasswordFormatValid()) return "password" - if (!isNickNameFormatValid()) return "nick name" - if (!isMbtiFormatValid()) return "mbti" - if (!isIntroFormatValid()) return "intro" - return " " - } } \ No newline at end of file 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 deleted file mode 100644 index 84e06bf..0000000 --- a/app/src/main/java/org/sopt/dosopttemplate/util/ToastMaker.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.sopt.dosopttemplate.util - -import android.content.Context -import android.widget.Toast - -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() - } -} \ 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..3e70a53 --- /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,12}\$") + 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/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"> - + + + - + app:layout_constraintTop_toBottomOf="@id/tv_id"> + + + + app:layout_constraintTop_toBottomOf="@id/til_id" /> - + app:layout_constraintTop_toBottomOf="@id/tv_password"> + + + + + + - + app:layout_constraintTop_toBottomOf="@id/tv_id"> + + + + + app:layout_constraintTop_toBottomOf="@id/til_id" /> - + app:layout_constraintTop_toBottomOf="@id/tv_password"> + + + + app:layout_constraintTop_toBottomOf="@id/til_password" /> - + app:layout_constraintTop_toBottomOf="@id/tv_nickName"> + + + + app:layout_constraintTop_toBottomOf="@id/til_nickName" /> - + app:layout_constraintTop_toBottomOf="@id/tv_mbti"> + + + + app:layout_constraintTop_toBottomOf="@id/til_mbti" /> - + app:layout_constraintTop_toBottomOf="@id/tv_intro"> + + + + app:layout_constraintTop_toBottomOf="@id/til_intro" /> - + app:layout_constraintTop_toBottomOf="@id/tv_music"> + + + + app:layout_constraintTop_toBottomOf="@id/til_music" /> - + app:layout_constraintTop_toBottomOf="@id/tv_artist"> + + + @@ -192,6 +241,8 @@ android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginEnd="20dp" + android:enabled="@{authViewModel.loginConditionSatisfied}" + android:onClick="@{() -> authViewModel.signUp()}" android:text="@string/signUp_btn" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/fragment_doandroid.xml b/app/src/main/res/layout/fragment_doandroid.xml index 4f7a7c3..63bc086 100644 --- a/app/src/main/res/layout/fragment_doandroid.xml +++ b/app/src/main/res/layout/fragment_doandroid.xml @@ -15,13 +15,11 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 24c4884..481dc1e 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -3,4 +3,5 @@ #FF000000 #FFFFFFFF #808080 + #D0312D \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6a7a534..abf14f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,4 +33,14 @@ Edit My Page 완료 + 로그인에 성공하였고 유저 아이디는 + 입니다. + 서버 에러 발생 + + 회원가입 성공! + 회원가입 실패. + + 로그인 버튼이 클릭 되었습니다. + 로그인 버튼을 클릭해주세요. + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 382ffb1..b4aa25a 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -6,4 +6,12 @@ + \ No newline at end of file