diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7c509aa..e19a2ad 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,6 +5,8 @@ plugins { alias(libs.plugins.jetbrains.kotlin.android) id ("kotlin-kapt") id("kotlin-parcelize") + id("com.google.devtools.ksp") + id("com.google.dagger.hilt.android") } android { @@ -100,4 +102,12 @@ dependencies { // glide implementation(libs.glide) -} \ No newline at end of file + + // hilt + implementation(libs.hilt.android) + ksp(libs.hilt.android.compiler) + + implementation(libs.androidx.lifecycle.viewmodel.ktx.v291) + implementation(libs.androidx.lifecycle.livedata.ktx) + implementation(libs.androidx.fragment.ktx) +} diff --git a/app/src/main/java/com/simply407/patpat/data/api/patApi.kt b/app/src/main/java/com/simply407/patpat/data/api/PatPatService.kt similarity index 93% rename from app/src/main/java/com/simply407/patpat/data/api/patApi.kt rename to app/src/main/java/com/simply407/patpat/data/api/PatPatService.kt index 55f4503..12a06e7 100644 --- a/app/src/main/java/com/simply407/patpat/data/api/patApi.kt +++ b/app/src/main/java/com/simply407/patpat/data/api/PatPatService.kt @@ -22,7 +22,11 @@ import retrofit2.http.PUT import retrofit2.http.Path import retrofit2.http.Query -interface patApi { +// TODO 대문자, API보단 Service 라는 표현이 더 바람직해보임 + +// kotlin 공식 coding convention +// - 클래스와 객체의 이름은 대문자 카멜 표기법을 사용합니다. +interface PatPatService { @POST("api/v1/login") suspend fun postLogin(@Body loginRequest: LoginRequest): Response diff --git a/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt b/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt index 72b2abe..96d5ec2 100644 --- a/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt +++ b/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt @@ -3,17 +3,30 @@ package com.simply407.patpat.data.api import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory +// TODO object keyword로 선언하면 어떤일이 발생할까?? +// TODO eager vs lazy + +// object +// - 싱글톤 패턴을 구현할 때 사용 +// - 해당 object가 처음 접근될 때 지연 초기화(lazy initialization)됩니다 + +// Eager Initialization (즉시 초기화) +// - 프로그램 시작 시 또는 클래스가 로드될 때 바로 인스턴스를 생성하는 방식 +// - 장점: 인스턴스가 항상 준비되어 있어 접근 시 지연이 없습니다 +// - 단점: 사용되지 않을 수도 있는 인스턴스에 대해 미리 자원을 할당하게 됩니다 + object RetrofitInstance { private const val BASE_URL = "https://oognuyh.asuscomm.com/" - private val client = Retrofit + val client: Retrofit = Retrofit .Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() + // TODO client의 선언을 val로 하면 되면 좋지 않을까?? fun getInstance(): Retrofit { return client } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt b/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt index 9d6f76c..5796ccd 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt @@ -1,12 +1,6 @@ package com.simply407.patpat.data.model -data class CounselorMbtiInfo(val name: String, val mbtiList: List) - -object MBTIRepository { - val CounselorMbtiDataList = listOf( - CounselorMbtiInfo("복남이", listOf("INFJ", "INFP", "ENFJ", "ENFP")), - CounselorMbtiInfo("닥터 냉철한", listOf("INTJ", "INTP", "ENTJ", "ENTP")), - CounselorMbtiInfo("곽두팔", listOf("ISTJ", "ISTP", "ESTJ", "ESTP")), - CounselorMbtiInfo("코코", listOf("ISFJ", "ISFP", "ESFJ", "ESFP")) - ) -} \ No newline at end of file +data class CounselorMbtiInfo( + val name: String, + val mbtiList: List +) diff --git a/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt b/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt index 9d0e2c6..5aeb10b 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt @@ -3,6 +3,16 @@ package com.simply407.patpat.data.model import android.os.Parcelable import kotlinx.parcelize.Parcelize +// TODO API data class 와 UI data class는 분리하는게 좋다. isLiked는 api에서 처리? +// TODO isLiked 는 var로 해야하나? val로 선언하는 방법은? mutable vs immutable + +// API data class 와 UI data class는 분리하는게 좋다 +// - 관심사 분리 +// - 유지보수 하기 좋다 + +// API 응답은 Immutable하게 val로 받는 것이 일반적 + +// CreateLetterResponse -> LetterResponse 이라는 이름이 더 좋음 @Parcelize data class CreateLetterResponse( val id: String, @@ -10,7 +20,19 @@ data class CreateLetterResponse( val footer: String?, val counselorId: String, val userId: String, - var isLiked: Boolean, + val isLiked: Boolean, val createdAt: String, val updatedAt: String ) : Parcelable + +// UI data class +// - API data 로 받아온 걸 UI data 형태로 데이터 변환(Mapping) 해서 표현되게 해줘야 함 +data class LetterUiModel( + val id: String, + val content: String, + val footer: String?, + val counselorName: String, + val userName: String, + var isLiked: Boolean, + val displayDate: String +) diff --git a/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt b/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt index 9ec7c11..a7d4750 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt @@ -1,8 +1,26 @@ package com.simply407.patpat.data.model +// TODO data 가 null이 되면 어떤일이 벌어질까? totalElements 이 null이 되면 어떤일이 벌어질까? +// TODO 원시타입 vs 참조타입 + +// JsonDataException 또는 JsonParseException) 가 발생합니다. +// - non nullable -> nullable 변경해주기 +// ex) val data: List -> val data: List? + +// 원시타입 +// - 기본 데이터 타입으로, 메모리에 직접 값을 저장 +// - ex) Int, Long, Float, Double, Boolean, Char, Byte, Short 등 +// - null 값을 가질 수 없음 + +// 참조타입 +// - 객체의 메모리 주소를 저장 +// - ex) String, List, Map, Array, Class 등 +// - null 값을 가질 수 있음 +// - Int?, Boolean? → Kotlin에서 nullable한 원시 타입도 결국 참조 타입으로 처리됨 + data class GetAllLettersResponse( - val data: List, - val totalElements: Int, + val data: List?, + val totalElements: Int?, val currentPage: Int, val totalPages: Int ) diff --git a/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt b/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt index 9a1c69e..d9799aa 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt @@ -3,6 +3,13 @@ package com.simply407.patpat.data.model import android.content.Context import android.content.SharedPreferences +// TODO Preference는 MainThread? IOThread? WorkerThread? +// - MainThread 에서 SharedPreferences 작업을 요청하고, 실제 파일은 IOThread 에서 실행 + +// - MainThread : UI와 상호작용하는 메인 스레드 +// - IOThread : 입출력 작업을 수행하는 스레드 +// - WorkerThread : 일반적인 계산이나 비동기 작업을 수행하는 스레드 +// ex) bit map 처리 같은 무거운 작업을 할 때, 사용 되면 좋음 object SharedPreferencesManager { const val FILE_NAME = "user_info" @@ -68,4 +75,4 @@ object SharedPreferencesManager { return sharedPref.getString(KEY_COUNSELOR_RECOMMENDATION, null) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt index 77e22fc..4d18fe1 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt @@ -1,17 +1,49 @@ package com.simply407.patpat.data.repository +import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance +import com.simply407.patpat.data.model.ChattingRoomInfo +import com.simply407.patpat.data.model.MessageInfo import com.simply407.patpat.data.model.PostMessageRequest +import retrofit2.Response +import javax.inject.Inject +import javax.inject.Singleton class ChattingRepository { + // TODO 의존성주입을 하면 더 좋을 것 같다. hilt를 사용해 + // TODO create 의 내부 구현을 확인해보면 좋을듯 + // TODO API return형은 Response data class 를 반환 할 수 있게 repository에서 구현로직 들어가야함 + private val patPatService: PatPatService = RetrofitInstance.client.create(PatPatService::class.java) - val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) + suspend fun getAllChattingRoomInfo(accessToken: String, counselorId: String, page: Int, size: Int) = patPatService.getAllChattingRoomInfo(accessToken, counselorId, page, size) - suspend fun getAllChattingRoomInfo(accessToken: String, counselorId: String, page: Int, size: Int) = patApi.getAllChattingRoomInfo(accessToken, counselorId, page, size) + suspend fun postMessage( + accessToken: String, + counselorId: String, + postMessageRequest: PostMessageRequest + ) = patPatService.postMessage(accessToken, counselorId, postMessageRequest) +} + +// Hilt에 결합 정보를 제공하는 한 가지 방법은 생성자 삽입입니다 +// 클래스의 생성자에서 @Inject 주석을 사용하여 클래스의 인스턴스를 제공하는 방법을 Hilt에 알려줍니다 +@Singleton +class ChattingRepository @Inject constructor( + private val patPatService: PatPatService +) { + suspend fun getAllChattingRoomInfo( + accessToken: String, + counselorId: String, + page: Int, + size: Int + ): Response { + return patPatService.getAllChattingRoomInfo(accessToken, counselorId, page, size) + } suspend fun postMessage( accessToken: String, counselorId: String, postMessageRequest: PostMessageRequest - ) = patApi.postMessage(accessToken, counselorId, postMessageRequest) -} \ No newline at end of file + ): Response { + return patPatService.postMessage(accessToken, counselorId, postMessageRequest) + } +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt index 6a6211e..7059203 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt @@ -1,10 +1,11 @@ package com.simply407.patpat.data.repository +import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance class CounselorRepository { - val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) + private val patPatService: PatPatService = RetrofitInstance.client.create(PatPatService::class.java) - suspend fun getCounselors(accessToken: String) = patApi.getCounselors(accessToken) -} \ No newline at end of file + suspend fun getCounselors(accessToken: String) = patPatService.getCounselors(accessToken) +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt index 4a4617e..11885c9 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt @@ -1,22 +1,23 @@ package com.simply407.patpat.data.repository +import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance import com.simply407.patpat.data.model.CreateLetterRequest import com.simply407.patpat.data.model.LikeLetterRequest class LetterRepository { - val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) + private val patPatService: PatPatService = RetrofitInstance.client.create(PatPatService::class.java) suspend fun createLetter(accessToken: String, createLetterRequest: CreateLetterRequest) = - patApi.createLetter(accessToken, createLetterRequest) + patPatService.createLetter(accessToken, createLetterRequest) suspend fun getAllLetters(accessToken: String, page: Int, size: Int, isLiked: Boolean) = - patApi.getAllLetters(accessToken, page, size, isLiked) + patPatService.getAllLetters(accessToken, page, size, isLiked) suspend fun likeLetter( accessToken: String, letterId: String, likeLetterRequest: LikeLetterRequest - ) = patApi.likeLetter(accessToken, letterId, likeLetterRequest) -} \ No newline at end of file + ) = patPatService.likeLetter(accessToken, letterId, likeLetterRequest) +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt index abca8b5..86b4081 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt @@ -1,16 +1,17 @@ package com.simply407.patpat.data.repository +import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance import com.simply407.patpat.data.model.LoginRequest class LoginRepository { - val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) + private val patPatService: PatPatService = RetrofitInstance.client.create(PatPatService::class.java) - suspend fun postLogin(loginRequest: LoginRequest) = patApi.postLogin(loginRequest) + suspend fun postLogin(loginRequest: LoginRequest) = patPatService.postLogin(loginRequest) - suspend fun userLogout(accessToken: String) = patApi.userLogout(accessToken) + suspend fun userLogout(accessToken: String) = patPatService.userLogout(accessToken) - suspend fun userWithdrawal(accessToken: String) = patApi.userWithdrawal(accessToken) -} \ No newline at end of file + suspend fun userWithdrawal(accessToken: String) = patPatService.userWithdrawal(accessToken) +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/MBTIRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/MBTIRepository.kt new file mode 100644 index 0000000..122ddc9 --- /dev/null +++ b/app/src/main/java/com/simply407/patpat/data/repository/MBTIRepository.kt @@ -0,0 +1,44 @@ +package com.simply407.patpat.data.repository + +import android.content.Context +import com.simply407.patpat.R +import com.simply407.patpat.data.model.CounselorMbtiInfo + +// TODO 샘플코드이긴 하지만... 만약 String을 객체로 담고 싶다면?? +class MBTIRepository(context: Context) { + val counselorMbtiDataList = listOf( + CounselorMbtiInfo( + context.getString(R.string.counselor_boknam), + listOf( + context.getString(R.string.mbti_infj), + context.getString(R.string.mbti_infp), + context.getString(R.string.mbti_enfj), + context.getString(R.string.mbti_enfp) + ) + ), CounselorMbtiInfo( + context.getString(R.string.counselor_doctor), + listOf( + context.getString(R.string.mbti_intj), + context.getString(R.string.mbti_intp), + context.getString(R.string.mbti_entj), + context.getString(R.string.mbti_entp) + ) + ), CounselorMbtiInfo( + context.getString(R.string.counselor_kwak), + listOf( + context.getString(R.string.mbti_istj), + context.getString(R.string.mbti_istp), + context.getString(R.string.mbti_estj), + context.getString(R.string.mbti_estp) + ) + ), CounselorMbtiInfo( + context.getString(R.string.counselor_coco), + listOf( + context.getString(R.string.mbti_isfj), + context.getString(R.string.mbti_isfp), + context.getString(R.string.mbti_esfj), + context.getString(R.string.mbti_esfp) + ) + ) + ) +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt index 78b284f..5279a00 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt @@ -1,14 +1,15 @@ package com.simply407.patpat.data.repository +import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance import com.simply407.patpat.data.model.NewUserInfo class UserInfoRepository { - val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) + private val patPatService: PatPatService = RetrofitInstance.client.create(PatPatService::class.java) - suspend fun getUserInfo(accessToken: String) = patApi.getUserInfo(accessToken) + suspend fun getUserInfo(accessToken: String) = patPatService.getUserInfo(accessToken) suspend fun putUserInfo(accessToken: String, newUserInfo: NewUserInfo) = - patApi.putUserInfo(accessToken, newUserInfo) -} \ No newline at end of file + patPatService.putUserInfo(accessToken, newUserInfo) +} diff --git a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt index 6f43035..3c84cac 100644 --- a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt +++ b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt @@ -3,13 +3,19 @@ package com.simply407.patpat.ui.chatting import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.simply407.patpat.R import com.simply407.patpat.data.model.MessageInfo import com.simply407.patpat.databinding.ItemChattingCounselorBinding import com.simply407.patpat.databinding.ItemChattingMyBinding +import com.simply407.patpat.ui.chatting.ChattingAdapter.Companion.TYPE_ONE +import com.simply407.patpat.ui.chatting.ChattingAdapter.Companion.TYPE_TWO import com.simply407.patpat.ui.main.MainActivity +// TODO allMessagesDataList는 val로 선언 +// TODO ListAdapter 공부 필요 class ChattingAdapter(private var allMessagesDataList: MutableList, private val counselorId: Int) : RecyclerView.Adapter() { inner class ChattingCounselorViewHolder(private val binding: ItemChattingCounselorBinding) : RecyclerView.ViewHolder(binding.root) { @@ -116,6 +122,9 @@ class ChattingAdapter(private var allMessagesDataList: MutableList, fun updateMessages(newMessages: List) { allMessagesDataList = newMessages.toMutableList() + // TODO notifyDataSetChanged는 최악의 갱신 method() + // - 모든 데이터들을 모두 갱신하는 method() + // - 데이터가 많아지면 비효율적, 깜빡이는 현상 같은게 생김 notifyDataSetChanged() val mainActivity = MainActivity() @@ -123,8 +132,129 @@ class ChattingAdapter(private var allMessagesDataList: MutableList, } companion object { + // TODO 접근제어자 공부 필요 + // 1. Private + // 2. Public + // 3. Protected + // 4. inrenal const val TYPE_ONE = 1 const val TYPE_TWO = 2 } -} \ No newline at end of file +} + +// ListAdapter 선언 +class ChattingListAdapter( + private val counselorId: Int +) : ListAdapter(MessageDiffCallback()) { + + inner class ChattingCounselorViewHolder(private val binding: ItemChattingCounselorBinding) : RecyclerView.ViewHolder(binding.root) { + + fun bind(message: MessageInfo) { + + // 상담사 프로필 이미지 설정 + when (counselorId) { + 0 -> { + binding.imageViewChattingCounselorItem.setImageResource(R.drawable.ic_profile_boknam) + } + 1 -> { + binding.imageViewChattingCounselorItem.setImageResource(R.drawable.ic_doctor_profile) + } + 2 -> { + binding.imageViewChattingCounselorItem.setImageResource(R.drawable.ic_kwak_profile) + } + 3 -> { + binding.imageViewChattingCounselorItem.setImageResource(R.drawable.ic_coco_profile) + } + } + + // 상담사 메시지 설정 + binding.textViewChattingCounselorItem.text = message.content + } + + } + + inner class ChattingMyViewHolder(private val binding: ItemChattingMyBinding) : RecyclerView.ViewHolder(binding.root) { + + fun bind(message: MessageInfo) { + + // 유저 말풍선 색상 설정 + when (counselorId) { + 0 -> { + binding.textViewChattingMyItem.setBackgroundResource(R.drawable.bg_bok_chatting_body) + binding.imageViewTailChattingMyItem.setImageResource(R.drawable.ic_bok_chatting_tail) + } + 1 -> { + binding.textViewChattingMyItem.setBackgroundResource(R.drawable.bg_doctor_chatting_body) + binding.imageViewTailChattingMyItem.setImageResource(R.drawable.ic_doctor_chatting_tail) + } + 2 -> { + binding.textViewChattingMyItem.setBackgroundResource(R.drawable.bg_kwak_chatting_body) + binding.imageViewTailChattingMyItem.setImageResource(R.drawable.ic_kwak_chatting_tail) + } + 3 -> { + binding.textViewChattingMyItem.setBackgroundResource(R.drawable.bg_coco_chatting_body) + binding.imageViewTailChattingMyItem.setImageResource(R.drawable.ic_coco_chatting_tail) + } + } + + // 유저 메시지 설정 + binding.textViewChattingMyItem.text = message.content + } + + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when(viewType) { + TYPE_ONE -> { + val binding = ItemChattingCounselorBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ChattingCounselorViewHolder(binding) + } + TYPE_TWO -> { + val binding = ItemChattingMyBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ChattingMyViewHolder(binding) + } + else -> throw IllegalArgumentException("Invalid view type") + } + } + + override fun getItemViewType(position: Int): Int { + return when (getItem(position).role) { + "ASSISTANT" -> TYPE_COUNSELOR + "USER" -> TYPE_USER + else -> throw IllegalArgumentException("Invalid view type") + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val message = getItem(position) + when (holder.itemViewType) { + TYPE_COUNSELOR -> { + (holder as ChattingCounselorViewHolder).bind(message) + } + TYPE_USER -> { + (holder as ChattingMyViewHolder).bind(message) + } + } + } + + // viewModel 에서 가져와서 observe patten 으로 갱신(submitList) 하도록 하는게 더 좋음 + fun submitNewMessages(newMessages: List) { + submitList(newMessages) + } + + class MessageDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: MessageInfo, newItem: MessageInfo): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: MessageInfo, newItem: MessageInfo): Boolean { + return oldItem == newItem + } + } + + companion object { + const val TYPE_COUNSELOR = 1 + const val TYPE_USER = 2 + } +} diff --git a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt index c65b055..cf170bb 100644 --- a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt +++ b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt @@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -24,15 +25,27 @@ import com.simply407.patpat.ui.main.MainActivity class ChattingFragment : Fragment() { + // TODO Activity와 Fragment 통신 하는 방법 공부 + // TODO Fragment 간 통신 하는 방법 공부 + // 1. ViewModel (가장 좋은 방식) + // 2. Bundle + + // TODO by lazy를 사용해보는건 어떠한가? lateinit var 개인적으로 최대한 사용을 안하는게 좋은것 같음 왜?? + // - lateinit var 으로 선언 한 다음 초기화 전에 접근을 할 경우 NPE 발생할 수 있음 + // - lateinit var는 var 만 가능해서, 초기화된 후에도 언제든지 값을 변경할 수 있음 + // - by lazy 는 val 로 선언해서 불변성을 유지시켜줌 + private lateinit var mainActivity: MainActivity private lateinit var binding: FragmentChatting2Binding - private lateinit var chattingAdapter: ChattingAdapter - - private lateinit var chattingViewModel: ChattingViewModel + private val chattingAdapter: ChattingAdapter by lazy { + ChattingAdapter(mutableListOf(), currentPageIndex) + } + private val chattingViewModel by viewModels() private var currentPageIndex: Int = 0 private var counselorId: String = "" + // TODO 접근제어자 val TAG = "ChattingFragment" override fun onCreateView( @@ -46,7 +59,8 @@ class ChattingFragment : Fragment() { currentPageIndex = arguments?.getInt("currentPageIndex") ?: 0 counselorId = arguments?.getString("counselorId") ?: "" - chattingViewModel = ViewModelProvider(this)[ChattingViewModel::class.java] + // TODO ViewModel ktx 공부 + // chattingViewModel = ViewModelProvider(this)[ChattingViewModel::class.java] chattingViewModel.getAllChattingRoomInfo(SharedPreferencesManager.getUserAccessToken()!!, counselorId, 1, 100) @@ -96,7 +110,7 @@ class ChattingFragment : Fragment() { } recyclerViewChatting.run { - chattingAdapter = ChattingAdapter(mutableListOf(), currentPageIndex) + // chattingAdapter = ChattingAdapter(mutableListOf(), currentPageIndex) adapter = chattingAdapter layoutManager = LinearLayoutManager(requireContext()) diff --git a/app/src/main/java/com/simply407/patpat/ui/home/HomeFragment.kt b/app/src/main/java/com/simply407/patpat/ui/home/HomeFragment.kt index 4bfe7f4..473b869 100644 --- a/app/src/main/java/com/simply407/patpat/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/simply407/patpat/ui/home/HomeFragment.kt @@ -9,8 +9,8 @@ import android.view.ViewGroup import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import com.simply407.patpat.R -import com.simply407.patpat.data.model.MBTIRepository import com.simply407.patpat.data.model.SharedPreferencesManager +import com.simply407.patpat.data.repository.MBTIRepository import com.simply407.patpat.databinding.FragmentHomeBinding import com.simply407.patpat.databinding.ItemCounselorBinding import com.simply407.patpat.ui.login.LoginViewModel @@ -24,6 +24,8 @@ class HomeFragment : Fragment() { private lateinit var loginViewModel: LoginViewModel private lateinit var homeViewModel: HomeViewModel + private lateinit var mbtiRepository: MBTIRepository + val TAG = "HomeFragment1" override fun onCreateView( @@ -42,6 +44,8 @@ class HomeFragment : Fragment() { homeViewModel.getCounselors(it) } + mbtiRepository = MBTIRepository(requireContext()) + observeData() initUi() clickCounselor() @@ -161,7 +165,7 @@ class HomeFragment : Fragment() { // 유저의 mbti 를 토대로 상담사 추천 private fun counselorRecommendation(userMbti: String) { // 사용자의 MBTI와 일치하는 상담사 찾기 - val recommendedCounselor = MBTIRepository.CounselorMbtiDataList.find { counselorInfo -> + val recommendedCounselor = mbtiRepository.counselorMbtiDataList.find { counselorInfo -> counselorInfo.mbtiList.contains(userMbti) } @@ -222,4 +226,4 @@ class HomeFragment : Fragment() { } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt b/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt index 22d4010..06d9026 100644 --- a/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt +++ b/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt @@ -31,6 +31,18 @@ class LetterViewModel: ViewModel() { fun createLetter(accessToken: String, createLetterRequest: CreateLetterRequest) { viewModelScope.launch { + // TODO postValue vs setValue, post를 하면 발생하는 상황도 함께 공부 + + // setValue + // - 메인(UI) 스레드에서만 호출 + // -> 메인 스레드가 아닌 다른 스레드에서 호출하면 IllegalStateException이 발생 + // - LiveData의 값을 즉시 동기적으로(synchronously) 업데이트 + + // postValue + // - 어떤 스레드에서든 호출 가능 + // - LiveData의 값을 즉시 업데이트하지 않고, 메인 스레드의 이벤트 큐에 값 업데이트 작업을 게시(post)합니다. + // 이 작업은 메인 스레드가 준비되었을 때 비동기적으로(asynchronously) 처리됩니다. + _isLoading.postValue(true) try { val response = letterRepository.createLetter(accessToken, createLetterRequest) @@ -84,4 +96,4 @@ class LetterViewModel: ViewModel() { } } -} \ 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 008fd4e..f60a113 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,5 +48,25 @@ PS. 웃음이 보약인기라!! 😝 PS. 새겨 들어라 😼 붕붕아 중요한 발표가 있어서 긴장된다고 했지이~? 그치만 잘 할 수 있을거라고 믿어~! 열심히 준비 했으니까 잘 할 수 있을 것 같아~! 정말 느낌이 좋다니까? 자신을 한 번 믿어봐~ 나는 항상 널 믿어~! 지금까지 쭉 잘 해왔듯이 앞으로도 잘할어야아~! 그래도 너무 긴장이 되면, 아이스크림을 먹어 봐! 우리 집에 놀러오면 직접 만든 아이스크림을 줄게. + 복남이 + 닥터 냉철한 + 곽두팔 + 코코 + INFJ + INFP + ENFJ + ENFP + INTJ + INTP + ENTJ + ENTP + ISTJ + ISTP + ESTJ + ESTP + ISFJ + ISFP + ESFJ + ESFP - \ No newline at end of file + diff --git a/build.gradle.kts b/build.gradle.kts index 5f3e9e5..721300a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,5 +2,6 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.jetbrains.kotlin.android) apply false - -} \ No newline at end of file + id("com.google.devtools.ksp") version "1.9.23-1.0.20" apply false + id("com.google.dagger.hilt.android") version "2.56.2" apply false +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8a90a0b..d665626 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,9 +1,13 @@ [versions] -agp = "8.5.0" +agp = "8.6.0" +androidxLifecycleViewmodelKtx = "2.9.1" circleimageview = "3.1.0" converterGson = "2.9.0" +fragmentKtx = "1.8.8" glide = "4.16.0" gson = "2.10.1" +hiltAndroid = "2.56.2" +hiltAndroidVersion = "2.56.2" jetbrainsKotlinxCoroutinesAndroid = "1.6.4" kotlin = "1.9.0" coreKtx = "1.13.1" @@ -34,8 +38,11 @@ v2All = "2.20.3" adapter-rxjava2 = { module = "com.squareup.retrofit2:adapter-rxjava2", version.ref = "converterGson" } adapter-rxjava3 = { module = "com.squareup.retrofit2:adapter-rxjava3", version.ref = "converterGson" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" } +androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidxLifecycleViewmodelKtx" } androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } androidx-lifecycle-viewmodel-ktx-v240 = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtxVersion" } +androidx-lifecycle-viewmodel-ktx-v291 = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidxLifecycleViewmodelKtx" } androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" } androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" } androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" } @@ -43,6 +50,8 @@ circleimageview = { module = "de.hdodenhof:circleimageview", version.ref = "circ converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" } glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } gson = { module = "com.google.code.gson:gson", version.ref = "gson" } +hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroidVersion" } +hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroidVersion" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }