From 81bb0e202da1826835f8e1ac908d81b4697bf12b Mon Sep 17 00:00:00 2001 From: MunJangHun <105299421+moondev03@users.noreply.github.com> Date: Wed, 7 Aug 2024 05:36:35 +0900 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=96=20=EA=B0=90=EC=A0=95=20=EB=B3=84?= =?UTF-8?q?=20=EC=9D=BC=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20&=20Custom=20Numbe?= =?UTF-8?q?r=20Picker=20(Only=20Year=20and=20Month)=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] 감정 비활성 이미지 추가 * [feat] 감정 별 일기 목록 조회 API 추가 * [feat] 감정 별 일기 목록 조회 UseCase 추가 * [feat] Add Context Extension | AlertDialog Resize * [feat] Add Custom NumberPicker | Year and Month * [feat] Add Restore Data Feature * [feat] 감정 별 일기 조회 페이지 구현 --- .../datasource/EmotionRemoteDataSource.kt | 9 ++ .../EmotionRemoteDataSourceImpl.kt | 25 +++ .../model/emotion/SearchEmotionResponseDTO.kt | 9 ++ .../data/repository/EmotionRepositoryImpl.kt | 47 ++++++ .../com/nabi/data/service/EmotionService.kt | 11 ++ .../domain/repository/EmotionRepository.kt | 10 ++ .../usecase/emotion/SearchEmotionUseCase.kt | 13 ++ .../com/nabi/nabi/di/EmotionUseCaseModule.kt | 11 +- .../nabi/nabi/extension/ContextExtension.kt | 34 ++++ .../diary/emotion/EmotionSearchAdapter.kt | 56 +++++++ .../diary/emotion/EmotionSearchFragment.kt | 146 ++++++++++++++++++ .../diary/emotion/EmotionSearchViewModel.kt | 67 ++++++++ .../statistics/DiaryStatisticsFragment.kt | 43 +++++- .../views/diary/view/SelectDiaryFragment.kt | 70 ++++++++- .../src/main/res/drawable/img_anger_gray.png | Bin 0 -> 1162 bytes .../main/res/drawable/img_anxiety_gray.png | Bin 0 -> 1229 bytes .../main/res/drawable/img_boredom_gray.png | Bin 0 -> 1102 bytes .../main/res/drawable/img_happiness_gray.png | Bin 0 -> 1253 bytes .../main/res/drawable/img_sadness_gray.png | Bin 0 -> 1142 bytes .../drawable/shape_non_day_date_picker_bg.xml | 6 + .../res/layout/dialog_non_day_date_picker.xml | 23 +++ .../res/layout/fragment_emotion_search.xml | 111 +++++++++++++ .../src/main/res/values/themes.xml | 21 +++ 23 files changed, 700 insertions(+), 12 deletions(-) create mode 100644 Nabi/data/src/main/java/com/nabi/data/model/emotion/SearchEmotionResponseDTO.kt create mode 100644 Nabi/domain/src/main/java/com/nabi/domain/usecase/emotion/SearchEmotionUseCase.kt create mode 100644 Nabi/presentation/src/main/java/com/nabi/nabi/extension/ContextExtension.kt create mode 100644 Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchAdapter.kt create mode 100644 Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchFragment.kt create mode 100644 Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchViewModel.kt create mode 100644 Nabi/presentation/src/main/res/drawable/img_anger_gray.png create mode 100644 Nabi/presentation/src/main/res/drawable/img_anxiety_gray.png create mode 100644 Nabi/presentation/src/main/res/drawable/img_boredom_gray.png create mode 100644 Nabi/presentation/src/main/res/drawable/img_happiness_gray.png create mode 100644 Nabi/presentation/src/main/res/drawable/img_sadness_gray.png create mode 100644 Nabi/presentation/src/main/res/drawable/shape_non_day_date_picker_bg.xml create mode 100644 Nabi/presentation/src/main/res/layout/dialog_non_day_date_picker.xml create mode 100644 Nabi/presentation/src/main/res/layout/fragment_emotion_search.xml diff --git a/Nabi/data/src/main/java/com/nabi/data/datasource/EmotionRemoteDataSource.kt b/Nabi/data/src/main/java/com/nabi/data/datasource/EmotionRemoteDataSource.kt index b2c0c82..118d616 100644 --- a/Nabi/data/src/main/java/com/nabi/data/datasource/EmotionRemoteDataSource.kt +++ b/Nabi/data/src/main/java/com/nabi/data/datasource/EmotionRemoteDataSource.kt @@ -1,10 +1,19 @@ package com.nabi.data.datasource import com.nabi.data.model.BaseResponse +import com.nabi.data.model.PageableResponse import com.nabi.data.model.emotion.DiaryStatisticsResponseDTO +import com.nabi.data.model.emotion.SearchEmotionResponseDTO interface EmotionRemoteDataSource { suspend fun getDiaryStatistics(accessToken: String, startDate: String, endDate: String): Result> + suspend fun searchDiaryByEmotion( + accessToken: String, + emotion: String, + page: Int, + size: Int, + sort: String + ): Result>> } \ No newline at end of file diff --git a/Nabi/data/src/main/java/com/nabi/data/datasourceImpl/EmotionRemoteDataSourceImpl.kt b/Nabi/data/src/main/java/com/nabi/data/datasourceImpl/EmotionRemoteDataSourceImpl.kt index a5c94b0..45abed1 100644 --- a/Nabi/data/src/main/java/com/nabi/data/datasourceImpl/EmotionRemoteDataSourceImpl.kt +++ b/Nabi/data/src/main/java/com/nabi/data/datasourceImpl/EmotionRemoteDataSourceImpl.kt @@ -2,7 +2,9 @@ package com.nabi.data.datasourceImpl import com.nabi.data.datasource.EmotionRemoteDataSource import com.nabi.data.model.BaseResponse +import com.nabi.data.model.PageableResponse import com.nabi.data.model.emotion.DiaryStatisticsResponseDTO +import com.nabi.data.model.emotion.SearchEmotionResponseDTO import com.nabi.data.service.EmotionService import javax.inject.Inject @@ -32,4 +34,27 @@ class EmotionRemoteDataSourceImpl @Inject constructor( } } + override suspend fun searchDiaryByEmotion( + accessToken: String, + emotion: String, + page: Int, + size: Int, + sort: String + ): Result>> { + return try { + val response = emotionService.searchDiaryByEmotion(accessToken, emotion, page, size, sort) + if (response.isSuccessful) { + val emotionResponse = response.body() + if (emotionResponse != null) { + Result.success(emotionResponse) + } else { + Result.failure(Exception("Search Emotion failed: response body is null")) + } + } else { + Result.failure(Exception("Search Emotion failed: ${response.message()}")) + } + } catch (e: Exception) { + Result.failure(e) + } + } } \ No newline at end of file diff --git a/Nabi/data/src/main/java/com/nabi/data/model/emotion/SearchEmotionResponseDTO.kt b/Nabi/data/src/main/java/com/nabi/data/model/emotion/SearchEmotionResponseDTO.kt new file mode 100644 index 0000000..8f839c4 --- /dev/null +++ b/Nabi/data/src/main/java/com/nabi/data/model/emotion/SearchEmotionResponseDTO.kt @@ -0,0 +1,9 @@ +package com.nabi.data.model.emotion + +import com.google.gson.annotations.SerializedName + +data class SearchEmotionResponseDTO( + @SerializedName("diaryEntryDate") val diaryEntryDate: String, + @SerializedName("content") val content: String, + @SerializedName("diaryId") val diaryId: Int +) \ No newline at end of file diff --git a/Nabi/data/src/main/java/com/nabi/data/repository/EmotionRepositoryImpl.kt b/Nabi/data/src/main/java/com/nabi/data/repository/EmotionRepositoryImpl.kt index 019e11f..94d85fa 100644 --- a/Nabi/data/src/main/java/com/nabi/data/repository/EmotionRepositoryImpl.kt +++ b/Nabi/data/src/main/java/com/nabi/data/repository/EmotionRepositoryImpl.kt @@ -1,6 +1,8 @@ package com.nabi.data.repository import com.nabi.data.datasource.EmotionRemoteDataSource +import com.nabi.domain.model.PageableInfo +import com.nabi.domain.model.diary.SearchDiary import com.nabi.domain.model.emotion.EmotionStatistics import com.nabi.domain.repository.EmotionRepository import javax.inject.Inject @@ -33,4 +35,49 @@ class EmotionRepositoryImpl @Inject constructor( Result.failure(result.exceptionOrNull() ?: Exception("Unknown error")) } } + + override suspend fun searchDiaryByEmotion( + accessToken: String, + emotion: String, + page: Int, + size: Int, + sort: String + ): Result>> { + val result = emotionRemoteDataSource.searchDiaryByEmotion(accessToken, emotion, page, size, sort) + + return if (result.isSuccess) { + val res = result.getOrNull() + if (res != null) { + val data = res.data + if (data != null) { + val pageableInfo = PageableInfo( + totalPages = data.totalPages, + totalElements = data.totalElements, + elementSize = data.size, + currentPageNumber = data.number, + isLastPage = data.last + ) + + if (data.size == 0) { + Result.success(Pair(pageableInfo, emptyList())) + } else { + val searchDiaryList = data.content.map { + SearchDiary( + it.content, + it.diaryEntryDate, + it.diaryId + ) + } + Result.success(Pair(pageableInfo, searchDiaryList)) + } + } else { + Result.failure(Exception("Search Emotion Data is null")) + } + } else { + Result.failure(Exception("Search Emotion Data Failed: response body is null")) + } + } else { + Result.failure(result.exceptionOrNull() ?: Exception("Unknown error")) + } + } } \ No newline at end of file diff --git a/Nabi/data/src/main/java/com/nabi/data/service/EmotionService.kt b/Nabi/data/src/main/java/com/nabi/data/service/EmotionService.kt index bd72c4b..ceebcdc 100644 --- a/Nabi/data/src/main/java/com/nabi/data/service/EmotionService.kt +++ b/Nabi/data/src/main/java/com/nabi/data/service/EmotionService.kt @@ -1,11 +1,14 @@ package com.nabi.data.service import com.nabi.data.model.BaseResponse +import com.nabi.data.model.PageableResponse import com.nabi.data.model.emotion.DiaryStatisticsResponseDTO +import com.nabi.data.model.emotion.SearchEmotionResponseDTO import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Header import retrofit2.http.Path +import retrofit2.http.Query interface EmotionService { @@ -16,4 +19,12 @@ interface EmotionService { @Path("endDate") endDate: String ): Response> + @GET("/emotion/search") + suspend fun searchDiaryByEmotion( + @Header("Authorization") accessToken: String, + @Query("emotion") emotion: String, + @Query("page") page: Int, + @Query("size") size: Int, + @Query("sort") sort: String + ): Response>> } \ No newline at end of file diff --git a/Nabi/domain/src/main/java/com/nabi/domain/repository/EmotionRepository.kt b/Nabi/domain/src/main/java/com/nabi/domain/repository/EmotionRepository.kt index 526ffe2..667bcb8 100644 --- a/Nabi/domain/src/main/java/com/nabi/domain/repository/EmotionRepository.kt +++ b/Nabi/domain/src/main/java/com/nabi/domain/repository/EmotionRepository.kt @@ -1,9 +1,19 @@ package com.nabi.domain.repository +import com.nabi.domain.model.PageableInfo +import com.nabi.domain.model.diary.SearchDiary import com.nabi.domain.model.emotion.EmotionStatistics interface EmotionRepository { suspend fun getDiaryStatistics(accessToken: String, startDate: String, endDate: String): Result + suspend fun searchDiaryByEmotion( + accessToken: String, + emotion: String, + page: Int, + size: Int, + sort: String + ): Result>> + } \ No newline at end of file diff --git a/Nabi/domain/src/main/java/com/nabi/domain/usecase/emotion/SearchEmotionUseCase.kt b/Nabi/domain/src/main/java/com/nabi/domain/usecase/emotion/SearchEmotionUseCase.kt new file mode 100644 index 0000000..75e0608 --- /dev/null +++ b/Nabi/domain/src/main/java/com/nabi/domain/usecase/emotion/SearchEmotionUseCase.kt @@ -0,0 +1,13 @@ +package com.nabi.domain.usecase.emotion + +import com.nabi.domain.model.PageableInfo +import com.nabi.domain.model.diary.DiaryInfo +import com.nabi.domain.model.diary.SearchDiary +import com.nabi.domain.model.emotion.EmotionStatistics +import com.nabi.domain.repository.EmotionRepository + +class SearchEmotionUseCase(private val repository: EmotionRepository) { + suspend operator fun invoke(accessToken: String, emotion: String, page: Int, size: Int, sort: String): Result>>{ + return repository.searchDiaryByEmotion("Bearer $accessToken", emotion, page, size, sort) + } +} \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/di/EmotionUseCaseModule.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/di/EmotionUseCaseModule.kt index a97352a..ad4be4c 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/di/EmotionUseCaseModule.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/di/EmotionUseCaseModule.kt @@ -2,6 +2,7 @@ package com.nabi.nabi.di import com.nabi.domain.repository.EmotionRepository import com.nabi.domain.usecase.emotion.GetEmotionStatisticsUseCase +import com.nabi.domain.usecase.emotion.SearchEmotionUseCase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -14,9 +15,17 @@ object EmotionUseCaseModule { @Provides @Singleton - fun provideHomeUseCase( + fun provideGetEmotionStatisticsUseCase( repository: EmotionRepository ): GetEmotionStatisticsUseCase { return GetEmotionStatisticsUseCase(repository = repository) } + + @Provides + @Singleton + fun provideSearchEmotionUseCase( + repository: EmotionRepository + ): SearchEmotionUseCase { + return SearchEmotionUseCase(repository = repository) + } } \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/extension/ContextExtension.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/extension/ContextExtension.kt new file mode 100644 index 0000000..77a01cf --- /dev/null +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/extension/ContextExtension.kt @@ -0,0 +1,34 @@ +package com.nabi.nabi.extension + +import android.app.Dialog +import android.content.Context +import android.graphics.Point +import android.os.Build +import android.view.WindowManager + +fun Context.dialogResize(dialog: Dialog, width: Float, height: Float){ + val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager + + if (Build.VERSION.SDK_INT < 30){ + val display = windowManager.defaultDisplay + val size = Point() + + display.getSize(size) + + val window = dialog.window + + val x = (size.x * width).toInt() + val y = (size.y * height).toInt() + + window?.setLayout(x, y) + + } else{ + val rect = windowManager.currentWindowMetrics.bounds + + val window = dialog.window + val x = (rect.width() * width).toInt() + val y = (rect.height() * height).toInt() + + window?.setLayout(x, y) + } +} \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchAdapter.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchAdapter.kt new file mode 100644 index 0000000..0bfc998 --- /dev/null +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchAdapter.kt @@ -0,0 +1,56 @@ +package com.nabi.nabi.views.diary.emotion + +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.nabi.domain.model.diary.SearchDiary +import com.nabi.nabi.databinding.ItemSearchDiaryBinding +import com.nabi.nabi.views.OnRvItemClickListener + +class EmotionSearchAdapter : ListAdapter(diaryDiffUtil) { + + companion object { + private val diaryDiffUtil = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: SearchDiary, newItem: SearchDiary): Boolean = + oldItem.diaryId == newItem.diaryId + + override fun areContentsTheSame(oldItem: SearchDiary, newItem: SearchDiary): Boolean = + oldItem == newItem + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchResultViewHolder { + val binding = ItemSearchDiaryBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return SearchResultViewHolder(binding) + } + + override fun onBindViewHolder(holder: SearchResultViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + inner class SearchResultViewHolder(val binding: ItemSearchDiaryBinding) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: SearchDiary) { + binding.tvSearchDiaryDate.text = item.diaryEntryDate + binding.tvContent.text = item.previewContent + + itemView.setOnClickListener { + rvItemClickListener.onClick(item.diaryId) + } + } + } + + private lateinit var rvItemClickListener: OnRvItemClickListener + + fun setRvItemClickListener(rvItemClickListener: OnRvItemClickListener){ + this.rvItemClickListener = rvItemClickListener + } + + override fun submitList(list: List?) { + list?.let { + val filteredList = it.distinctBy { diary -> diary.diaryId } + super.submitList(filteredList) + } ?: super.submitList(null) + } +} diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchFragment.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchFragment.kt new file mode 100644 index 0000000..83e7f04 --- /dev/null +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchFragment.kt @@ -0,0 +1,146 @@ +package com.nabi.nabi.views.diary.emotion + +import androidx.core.content.ContextCompat +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.nabi.nabi.R +import com.nabi.nabi.base.BaseFragment +import com.nabi.nabi.custom.CustomDecoration +import com.nabi.nabi.databinding.FragmentEmotionSearchBinding +import com.nabi.nabi.utils.UiState +import com.nabi.nabi.views.MainActivity +import com.nabi.nabi.views.OnRvItemClickListener +import com.nabi.nabi.views.diary.detail.DetailDiaryFragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class EmotionSearchFragment( + private val emotion: String +): BaseFragment(R.layout.fragment_emotion_search) { + private val viewModel: EmotionSearchViewModel by viewModels() + private lateinit var emotionSearchAdapter: EmotionSearchAdapter + private var isLoading = false + + override fun initView() { + setSearchDiaryAdapter() + setCurrentEmotion() + viewModel.fetchData(emotion) + } + + override fun onResume() { + super.onResume() + + emotionSearchAdapter.submitList(viewModel.diaryItems.value) + } + + private fun setSearchDiaryAdapter(){ + emotionSearchAdapter = EmotionSearchAdapter().apply { + setRvItemClickListener(object : OnRvItemClickListener { + override fun onClick(item: Int) { + (requireActivity() as MainActivity).replaceFragment(DetailDiaryFragment(item), true) + } + }) + } + binding.rvEmotionDiaryResult.layoutManager = LinearLayoutManager(requireContext()) + binding.rvEmotionDiaryResult.addItemDecoration(CustomDecoration(0.5f, ContextCompat.getColor(requireContext(), R.color.gray2))) + binding.rvEmotionDiaryResult.adapter = emotionSearchAdapter + } + + override fun initListener() { + super.initListener() + + binding.ibBack.setOnClickListener { + requireActivity().supportFragmentManager.popBackStack() + } + + binding.rvEmotionDiaryResult.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + val lastVisibleItemPos = layoutManager.findLastVisibleItemPosition() + val itemTotalCount = recyclerView.adapter?.itemCount ?: 0 + + if (lastVisibleItemPos >= itemTotalCount - 6) { + if(!isLoading){ + isLoading = true + viewModel.fetchData(emotion) + } + } + } + }) + + // 행복, 우울, 화남, 불안, 지루 + binding.ivEmotionAnger.setOnClickListener { + viewModel.fetchData("화남", true) + setCurrentEmotion() + } + binding.ivEmotionHappiness.setOnClickListener { + viewModel.fetchData("행복", true) + setCurrentEmotion() + } + binding.ivEmotionBoredom.setOnClickListener { + viewModel.fetchData("지루", true) + setCurrentEmotion() + } + binding.ivEmotionSadness.setOnClickListener { + viewModel.fetchData("우울", true) + setCurrentEmotion() + } + binding.ivEmotionAnxiety.setOnClickListener { + viewModel.fetchData("불안", true) + setCurrentEmotion() + } + } + + private fun setCurrentEmotion(){ + val resourceIds = mutableListOf() + val condition = if(viewModel.searchEmotion.value.isNullOrEmpty()) emotion else viewModel.searchEmotion.value + + when(condition){ + "화남" -> resourceIds.addAll(listOf(R.drawable.img_anger, R.drawable.img_happiness_gray, R.drawable.img_boredom_gray, R.drawable.img_sadness_gray, R.drawable.img_anxiety_gray)) + "행복" -> resourceIds.addAll(listOf(R.drawable.img_anger_gray, R.drawable.img_happiness, R.drawable.img_boredom_gray, R.drawable.img_sadness_gray, R.drawable.img_anxiety_gray)) + "지루" -> resourceIds.addAll(listOf(R.drawable.img_anger_gray, R.drawable.img_happiness_gray, R.drawable.img_boredom, R.drawable.img_sadness_gray, R.drawable.img_anxiety_gray)) + "우울" -> resourceIds.addAll(listOf(R.drawable.img_anger_gray, R.drawable.img_happiness_gray, R.drawable.img_boredom_gray, R.drawable.img_sadness, R.drawable.img_anxiety_gray)) + "불안" -> resourceIds.addAll(listOf(R.drawable.img_anger_gray, R.drawable.img_happiness_gray, R.drawable.img_boredom_gray, R.drawable.img_sadness_gray, R.drawable.img_anxiety)) + } + + resourceIds.run { + binding.ivEmotionAnger.setImageResource(this[0]) + binding.ivEmotionHappiness.setImageResource(this[1]) + binding.ivEmotionBoredom.setImageResource(this[2]) + binding.ivEmotionSadness.setImageResource(this[3]) + binding.ivEmotionAnxiety.setImageResource(this[4]) + } + } + + override fun setObserver() { + super.setObserver() + + viewModel.searchEmotion.observe(viewLifecycleOwner){ + if(it.isNotEmpty()){ + emotionSearchAdapter.submitList(emptyList()) + } + } + + viewModel.uiState.observe(viewLifecycleOwner){ + when(it){ + is UiState.Loading -> {} + is UiState.Failure -> { + showToast("검색 실패: ${it.message}") + } + is UiState.Success -> { + isLoading = false + + if(it.data.isEmpty()) emotionSearchAdapter.submitList(emptyList()) + else { + val temp = emotionSearchAdapter.currentList.toMutableList() + temp.addAll(it.data) + emotionSearchAdapter.submitList(temp) + } + } + } + } + } +} \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchViewModel.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchViewModel.kt new file mode 100644 index 0000000..80f116b --- /dev/null +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/emotion/EmotionSearchViewModel.kt @@ -0,0 +1,67 @@ +package com.nabi.nabi.views.diary.emotion + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.nabi.domain.model.diary.SearchDiary +import com.nabi.domain.model.emotion.EmotionStatistics +import com.nabi.domain.repository.DataStoreRepository +import com.nabi.domain.usecase.emotion.GetEmotionStatisticsUseCase +import com.nabi.domain.usecase.emotion.SearchEmotionUseCase +import com.nabi.nabi.utils.LoggerUtils +import com.nabi.nabi.utils.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class EmotionSearchViewModel @Inject constructor( + private val searchEmotionUseCase: SearchEmotionUseCase, + private val dataStoreRepository: DataStoreRepository +) : ViewModel() { + + private var page = 0 + private val size = 15 + private val sort = "" + private var isFinish = false + + private var _searchEmotion = MutableLiveData("") + val searchEmotion: LiveData get() = _searchEmotion + + private val _uiState = MutableLiveData>>(UiState.Loading) + val uiState: LiveData>> get() = _uiState + + private val _diaryItems = MutableLiveData>(mutableListOf()) + val diaryItems: LiveData> get() = _diaryItems + + fun fetchData(emotion: String, isNewEmotion: Boolean = false) { + if(searchEmotion.value != emotion) resetPageable(emotion) + if(!isNewEmotion && isFinish) return + + _uiState.value = UiState.Loading + _searchEmotion.value = emotion + + viewModelScope.launch { + val accessToken = dataStoreRepository.getAccessToken().getOrNull().orEmpty() + + searchEmotionUseCase(accessToken, emotion, page, size, sort) + .onSuccess { + isFinish = it.first.isLastPage + page++ + + _diaryItems.value?.addAll(it.second) + _uiState.value = UiState.Success(it.second) + }.onFailure { e -> + _uiState.value = UiState.Failure(message = e.message.toString()) + } + } + } + + private fun resetPageable(content: String){ + _diaryItems.value?.clear() + _searchEmotion.value = content + page = 0 + isFinish = false + } +} \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/statistics/DiaryStatisticsFragment.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/statistics/DiaryStatisticsFragment.kt index 629ee2d..015b005 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/statistics/DiaryStatisticsFragment.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/statistics/DiaryStatisticsFragment.kt @@ -1,12 +1,15 @@ package com.nabi.nabi.views.diary.statistics import android.app.DatePickerDialog +import android.os.Bundle import androidx.fragment.app.viewModels import com.nabi.domain.model.emotion.EmotionStatistics import com.nabi.nabi.R import com.nabi.nabi.base.BaseFragment import com.nabi.nabi.databinding.FragmentStatisticsDiaryBinding import com.nabi.nabi.utils.UiState +import com.nabi.nabi.views.MainActivity +import com.nabi.nabi.views.diary.emotion.EmotionSearchFragment import dagger.hilt.android.AndroidEntryPoint import java.text.SimpleDateFormat import java.util.Calendar @@ -21,17 +24,40 @@ class DiaryStatisticsFragment: BaseFragment(R.la private var startDate: Calendar = Calendar.getInstance() private var endDate: Calendar = Calendar.getInstance() - override fun initView() { - endDate.time = calendar.time - calendar.add(Calendar.MONTH, -1) - startDate.time = calendar.time + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState == null) { + endDate.time = calendar.time + calendar.add(Calendar.MONTH, -1) + startDate.time = calendar.time + } + } + override fun initView() { binding.tvEndDate.text = dateFormat.format(endDate.time) binding.tvStartDate.text = dateFormat.format(startDate.time) fetchData() } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong("startDate", startDate.timeInMillis) + outState.putLong("endDate", endDate.timeInMillis) + } + + override fun onViewStateRestored(savedInstanceState: Bundle?) { + super.onViewStateRestored(savedInstanceState) + savedInstanceState?.let { + startDate.timeInMillis = it.getLong("startDate") + endDate.timeInMillis = it.getLong("endDate") + binding.tvStartDate.text = dateFormat.format(startDate.time) + binding.tvEndDate.text = dateFormat.format(endDate.time) + fetchData() + } + } + override fun initListener() { super.initListener() @@ -40,7 +66,13 @@ class DiaryStatisticsFragment: BaseFragment(R.la } binding.tvStartDate.setOnClickListener { showDatePickerDialog(true) } - binding.tvEndDate.setOnClickListener { showDatePickerDialog( false) } + binding.tvEndDate.setOnClickListener { showDatePickerDialog(false) } + + binding.ivEmotionAnger.setOnClickListener { (requireActivity() as MainActivity).replaceFragment(EmotionSearchFragment("화남"), true)} + binding.ivEmotionHappiness.setOnClickListener { (requireActivity() as MainActivity).replaceFragment(EmotionSearchFragment("행복"), true)} + binding.ivEmotionBoredom.setOnClickListener { (requireActivity() as MainActivity).replaceFragment(EmotionSearchFragment("지루"), true)} + binding.ivEmotionSadness.setOnClickListener { (requireActivity() as MainActivity).replaceFragment(EmotionSearchFragment("우울"), true)} + binding.ivEmotionAnxiety.setOnClickListener { (requireActivity() as MainActivity).replaceFragment(EmotionSearchFragment("불안"), true)} } private fun showDatePickerDialog(isStartDate: Boolean) { @@ -99,7 +131,6 @@ class DiaryStatisticsFragment: BaseFragment(R.la datePickerDialog.show() } - private fun fetchData() { viewmodel.fetchData( dateFormat.format(startDate.time).replace(".", "-"), diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/view/SelectDiaryFragment.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/view/SelectDiaryFragment.kt index d146b14..af57179 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/view/SelectDiaryFragment.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/view/SelectDiaryFragment.kt @@ -1,10 +1,14 @@ package com.nabi.nabi.views.diary.view +import android.app.AlertDialog +import android.view.LayoutInflater import androidx.core.content.res.ResourcesCompat import androidx.viewpager2.widget.ViewPager2 import com.nabi.nabi.R import com.nabi.nabi.base.BaseFragment +import com.nabi.nabi.databinding.DialogNonDayDatePickerBinding import com.nabi.nabi.databinding.FragmentSelectDiaryBinding +import com.nabi.nabi.extension.dialogResize import com.nabi.nabi.views.MainActivity import com.nabi.nabi.views.diary.search.SearchDiaryFragment import com.nabi.nabi.views.diary.statistics.DiaryStatisticsFragment @@ -21,9 +25,13 @@ import java.util.Date import java.util.Locale @AndroidEntryPoint -class SelectDiaryFragment: BaseFragment(R.layout.fragment_select_diary) { +class SelectDiaryFragment : BaseFragment(R.layout.fragment_select_diary) { private lateinit var calendarAdapter: SelectDiaryMonthCalendarStateAdapter + // Define min and max year + private val minYear = 1950 + private val maxYear = Calendar.getInstance().get(Calendar.YEAR) + override fun initView() { calendarAdapter = SelectDiaryMonthCalendarStateAdapter(requireActivity()) binding.vpCalendarMonth.adapter = calendarAdapter @@ -71,6 +79,10 @@ class SelectDiaryFragment: BaseFragment(R.layout.fra binding.ivEmotionBoredom.setOnClickListener { createEmotionTooltip("지루해").showAlignTop(binding.ivEmotionBoredom) } binding.ivEmotionSadness.setOnClickListener { createEmotionTooltip("슬퍼").showAlignTop(binding.ivEmotionSadness) } binding.ivEmotionAnxiety.setOnClickListener { createEmotionTooltip("불안해").showAlignTop(binding.ivEmotionAnxiety) } + + binding.tvCurrentMonth.setOnClickListener { + showNumberPickerDialog() + } } private fun updateCurrentMonthText(position: Int) { @@ -81,8 +93,8 @@ class SelectDiaryFragment: BaseFragment(R.layout.fra binding.tvCurrentMonth.text = currentMonth } - private fun createEmotionTooltip(text: String): Balloon{ - val balloon = createBalloon(context = requireContext()){ + private fun createEmotionTooltip(text: String): Balloon { + return createBalloon(context = requireContext()) { setHeight(42) setWidth(BalloonSizeSpec.WRAP) @@ -106,9 +118,57 @@ class SelectDiaryFragment: BaseFragment(R.layout.fra setLifecycleOwner(viewLifecycleOwner) build() } + } + + private fun showNumberPickerDialog() { + val currentCalendar = Calendar.getInstance() + val displayedMonthYear = binding.tvCurrentMonth.text.toString() + val dateFormat = SimpleDateFormat("MMMM yyyy", Locale.ENGLISH) + val displayedDate = dateFormat.parse(displayedMonthYear) + currentCalendar.time = displayedDate!! + + val year = currentCalendar.get(Calendar.YEAR) + val month = currentCalendar.get(Calendar.MONTH) + + val dialogBinding = DialogNonDayDatePickerBinding.inflate(LayoutInflater.from(requireContext())) + + dialogBinding.npYear.minValue = minYear + dialogBinding.npYear.maxValue = maxYear + dialogBinding.npYear.value = year + + dialogBinding.npMonth.minValue = 1 + dialogBinding.npMonth.maxValue = 12 + dialogBinding.npMonth.value = month + 1 + + val builder = AlertDialog.Builder(requireContext(), R.style.DialogTheme) + .setView(dialogBinding.root) + .setPositiveButton("확인") { _, _ -> + val selectedYear = dialogBinding.npYear.value + val selectedMonth = dialogBinding.npMonth.value - 1 + + val totalDisplayedMonths = (year * 12 + month) + val totalSelectedMonths = (selectedYear * 12 + selectedMonth) + val differenceInMonths = totalSelectedMonths - totalDisplayedMonths + + val currentPosition = binding.vpCalendarMonth.currentItem + val newPosition = currentPosition + differenceInMonths + binding.vpCalendarMonth.setCurrentItem(newPosition, false) + + currentCalendar.set(selectedYear, selectedMonth, 1) + binding.tvCurrentMonth.text = SimpleDateFormat("MMMM yyyy", Locale.ENGLISH).format(currentCalendar.time) + } + .setNegativeButton("취소") { _, _ -> } + .create() + + builder.show() + + val positiveButton = builder.getButton(AlertDialog.BUTTON_POSITIVE) + val negativeButton = builder.getButton(AlertDialog.BUTTON_NEGATIVE) + positiveButton?.setTextAppearance(requireContext(), R.style.DialogButtonStyle) + negativeButton?.setTextAppearance(requireContext(), R.style.DialogButtonStyle) - return balloon + builder.context.dialogResize(builder, 0.8f, 0.3f) } -} \ No newline at end of file +} diff --git a/Nabi/presentation/src/main/res/drawable/img_anger_gray.png b/Nabi/presentation/src/main/res/drawable/img_anger_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..6efb77f0ab1f480163e1f8ce860901702f3d8b4e GIT binary patch literal 1162 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yaTa()7BevDc!MzGQrl@Ofr5<1 zLGDfr>(0r%1aer?9eo`c7&i8E|4C#8a^*c;978G?-`>s6yKN=V5O}|Pok?W)rmJFZ z+6zS2OicM`xa8c=>x=qNocmyW)S}V`Xap+o`t{yXJU%|&KK}2&=g*fvSDE-<{(u@Y z6C*QIqddPyLbm_5<=^J{Iw}OLRd{#l$(FjfD0}($(~{obUtKE8#nkvHc>33^PXgD5 z-MH3zw>qxs@BXDz_wlhdI&8E(adoQF>v>DRWZ$`_ou$8b-R3W!mnsPCh?#Ip+x7i! zy`{SH|F1nZyY=;4oC|B?f!nKtw@qFA#jCnZ_5QEVTjiE-z8;;`Zysg1t}af4gJqfK ziNJL44PU=+7W?w%c$vUAzD4(1CzoU# zu;vOEY2L5N!NP-q^yZscnJXx(zr4`E*ocn4HyeoFzbh-HWtQuA<>V1hE0YBNLm_V* z7bvnOa@C#EEt&9a`JF?eTuibO0vvD@@>}xGyR?f=Z|5toR}j#so3u{mjEkcJQ0oPt zR^|A!rzd1hPhO}XU;suN*b_C@ev{*7ZEPqOXnUe3rV>!gHMK=bghi-8ynEq}Zq`nd z*>2T>UI79gOJ17TX>x{ZaHw2*qc^R{KnbX6a!K106Me$mR`A6mao4UDEdzC z+C8sj8mUwC+`c_eU9AF)(e2!qUe`Q+b84B!`mMKud!qVUn;af|h+Pxtf8=fCs~x8w zFH;b>q3f}&XifOKobS)8wZB$qyv_$|yt_MM@f+KB`-v*_0`h7C>`VRC+fI!=Ys`ncM1UM|%6En7+1`90<06RS? zc)37I&?*g3x_r;xc;?(RS4V{hDs!)F3;PqcK1GCuiLFJ^_VlGkuQvnJ#H_nVU;T}m zv@vGw;a{I$-L3a`R4DjqdMAwWoZp&BAFjNvm5&DMSh?)0r{_D(XiG6JCPwv_B~!P` z03*%e*2}HpJK0$q8<);6P_*;<aCC~jrf2+S5isZO6>ZPx&C@`s0vM5+o5ew*1+%Xnxc6kv?x{)Lz<@7X z-7;y{|X$i!^%jQ9UHUfK@KOdF&`i?%rQxB`m=OpxHUpJDlhnTtGU RJkJBkdb;|#taD0e0s!F(@@@bC literal 0 HcmV?d00001 diff --git a/Nabi/presentation/src/main/res/drawable/img_anxiety_gray.png b/Nabi/presentation/src/main/res/drawable/img_anxiety_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..687f7734404d6d96edaceb4dd473355df4352e5a GIT binary patch literal 1229 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yaTa()7BevDc!MzGQrl@Ofr5<1 zLGDfr>(0r%1aer?9eo`c7&i8E|4C#8a+5q=978G?-`rdz*WEVEvdgGD6esD|ps|AoCx z4hIU;AHC_BEqC~S?z3%dmA;M&27lK)l(03cjs#kK`3#>|;IS92fdU*n@d8gXl<&Il z%FYBz92RlW{IyKMV8QL4S6?E+43Av^tH0YDmpUUl-xa97#5ihdv&ZU98XPRE`n{Ge zXbA$DBBI}sbirxeBA{rO?Nt{`iN+=eh1*gthC7-UXA847HZ&P5-Kz>zI7hD|$)IWN zB%nebkdFl{_Ds%ud(a1H_wMsan(WFO`I#gu+!cchb;VLjD%9^rwO9uWoH!xXqB&=; zdo0k)S#ir2C%_m3a@B=P@4V*( zIj!2hW737DCI{Dh21f<LOC5*i#1_*rcasd~FHK!C%d`{-KBZdTsV_~tEo$8X=hy{`WI&!0bM zG~Rdj0S55dtr6Afr%V^0U65WczxSWqoj?xW8|*hTXBNJXo3vVWE-+daL^-~x-ZFKy z%**SNf8R`Na!B}L+L8Qj<4w=@Hb{6FKrCgLdE|QC(KX>u;(%%4z}5)snYF*S#4LQ6 z6_=6lqiN0AKbvENw|LZ_TO2NRCM*aTz~7rc9F7W1+da8pp@KkxShu{_)JN;eQ?!7o zqGi#yd8)6s8UWJ{>%vuAL^2brv^#-Ggkj-|-SMtD4Gs%#Nu}KUxMi{3^xgKMZon{( z(ph!lubj|(jTSzU=w=PJ^Vh!FUfTa2=z#}Cj#HYHFHQQg&wUXv@@mu^3|~aP+Z+uH z%d;1kN!$rs2a4J*@sw;8Z4pyh-EN?tBOLqsLR1&Fu06jJ=;31Z+Shh{QQ@J7rhoYy z_UnYRqr!!|Z}N2yX;=4$^wrg^aeUB}cb(ts(B{qAch{bu*mQuE%jtU2TvcCt|LXng zfbNde*>$aIJ}^mN$SKicO~0bo1+?TwMhJ)3rxe#04%-4(NNjmv`|`qtrx&O1mdU>U zd;M9FQ>Fe(-@X12qqAbg@1`sN{!IyewXbjU{dAD~svN!Mmj+DPH+9$g#aF(E3UGu} zpU&02x9s-x7(0r%1aer?9eo`c7&i8E|4C$JU|@di>Eakt!T9!WY~EutiMGJ)FTy6fs&?lF zE?}~n^Hd>qZZE=`X- zy)jULqfPsSU~1;#@7E6sURMY>Xu^6^!!vTR*y9s7ueUcjTsZ4lm3yk_+^eTIG&opJ z*DSuUyleB#uE4G)2ZOtM*CJO>PnlY@gRPNk+RdX?XD*_lO?;oioV62;QFM!J0hU5X#)#T z0}lra8w=BO1)sBXMHebDGc__c9%yh#a8NK%5ReeyfJh?=@A%{CeqU!v`qFo@3ls$Q z*rs|p^^34b?fBE{^fZB$i>WbNVA&HX7AcLg`H`v%)w!5>_teJgJWWUgN(Kh;s@ioeP=M z?BEP^EYLOC0@FUcsd@L}mMbt0_B`5eRp@s~^5(zNwF&|v)pi~)|E9laY;p+55kI}v z{l1-@UEQD0@87>?KQ03DDtlkp9bbLz_nYT${Us6nFN^1G z2S(T7xy^U>CVhLg?*IQc`^#Ay7u;^LiJn>0)*%M8bNQ7t@1A+0vz++C26v12MciUaTjfq`Tf1=)L|ZAdc0r85EuJ1 z{;q9+P*edQlexv;okaBtLj=5TbbWaD?o}igljCOf%UYb_Q>wZS zT%81T;o14Q>s0nE`eHe|$$|AO_ohIhx}wsh`lWf>mn#U|37gP6Y0cVi=1ljODhQMq z@AcZ$cCULQP^QHEte4UBi(O#0{oO93wu|0{z@#rx_G;==xy{oabpVs+pR5HcrE9}_ zY*`xFVdQ&MBb@0MroAZ2$lO literal 0 HcmV?d00001 diff --git a/Nabi/presentation/src/main/res/drawable/img_happiness_gray.png b/Nabi/presentation/src/main/res/drawable/img_happiness_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..0859799785026b2bcd2a9853e2f744e56381faf3 GIT binary patch literal 1253 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yaTa()7BevDc!MzGQrl@Ofr5<1 zLGDfr>(0r%1aer?9eo`c7&i8E|4C#8a;rRD978G?-`>63H^+>j?P2EC*XOdnAB$cp z&DV5@omt|)?fJ~b9y3?|P2!n%of~KX3^dI9^EKJ5YdQ)!DwivAfU3O$ytu&)V3v#_^tsw`bMQI1v`DO>8?ewk*>4^3R8!oxkPkW`s+UVezcPZL)%3}!-Q*EKda}5p_uA;<~@hmQH}p0L#_cF(JUGS?JZZTG5ee70kHlfwc9e$7y!qZ-~I*S5>*o(z+W zvYwt*0(54rKwE-5i&jMr?;@Em9*znI4j{CF-|70CH5{Th*%xMWF##!{jZi6w0!&il zpUL|44H^$uZtfFhVPZx?6pS$W|6rikMkfPws?PCn&rmgpMaO}DalEq@oM z3sm!vtz)aF?BY6LXdm40TOnH1((3fu|95h-g+>1Y1Fu3pC3V~K=J#5yyCVz~S#N%L zcji`AqjecyH*OFmmd@clMUl#m#S) z=*K+ncXd?ID3iVJZRvK?KcjT{evxc}i~|Mb8|Ur1ow7FY`Zdv0Rc~vyngCPO_xH_H zwA|je&dBvGx)zrRjQ;)npH8nae)(qk_BX&Zv+F+l#>cfi=QVFfbpxaH@Z07cZT~iJ ze&4TCzwg`gUFF+fz7g%-9Z_)bZMMMsP2ZPJzaMP5OS$gr%g?5)jSt>7X&jM>y4_j6 zNy+5I$u5_*Q(PPuIQp3?MooXXGJSiNXtFQRrEwF!`Xu;X-hOjl2rwgbi7uI%?Xknj zQDH}}`ro@r5#0qYZ?y%1x#E|Vqo>8;lU)}WSQ9@XE9JNRLe3-!ni(`iwRb0 c!0G;<{Kpioz9>$2GzW=!y85}Sb4q9e0Mg+j(*OVf literal 0 HcmV?d00001 diff --git a/Nabi/presentation/src/main/res/drawable/img_sadness_gray.png b/Nabi/presentation/src/main/res/drawable/img_sadness_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..290f0c4b6f1aa1c109b263e7d57da3b12e64a0ee GIT binary patch literal 1142 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yaTa()7BevDc!MzGQrl@Ofr5<1 zLGDfr>(0r%1aer?9eo`c7&i8E|4C#8a=ATS978G?-`WIksweOR_c|xR|;;&bx5Rp%gBr#>L0aWVEh$x^iit0Eb+>z)=n3sz?!* zrmV~MvYpk|i!_exbkpu?a@b(?w*0p75{cSf*&ykoE8qR~xRVvG^L*Cy)Yt!?U%G$$ z_pS)-*-Z`~?tigR`t0JZ3N(+0gN2QSiJ7U9vGKvS_stHx_t+bmz#<1ZD+5OBzQ7wWR93ct3hZeutYd^gSq}D(| zKxSY3s?CviPF}2L)`nW)_|aH;jq8iZm&KkUP%9Vw3eE%Babd#UJv=L~1(%AlG`%{% zZkyObb}lA$pfM5x9IMtls$ElxS$OSM@*)KRi|(TWS<^u*pcK&Y=aLGu*Itto1_l|} znHj%6-@d=bGvlBXi{6z#T~1FQoP${gRZu3DePwfaet!P{KfgYI{(N)?P=0r%`K;;5 z>$XqQsol3&?7V1e0ps^)u^YqscF)`=m%Tg}sDyRq&FG%E{v(&4Z~gLB(tg$7u#2#&)d03K(FAp>4{s`N4g680{4T}r*E9nBnEU~xJXgn|?08T=Te#qe4cU03?voH+D>0w>{EI z+Vqy`YoPa@FA09~ruy5t`0%pNhgxma=f?J}GmH{H7VGFV;lY|A}|>+<*B?_U!Z z2uyB|G;c>Y{7lj2h+p~DN>jJ9$)TWPm)GgQUmqndUsL{fZ|l22pzq$jTov1QssDUL z*8VsV7O73Mr+d$>ZG8~O#pJlz^u$HwX44bKU!#Or8yEcgt#Hb*^PU&b!7_H;rB|2S zK7Doj#WEia4wk!r_Jn8qF4nldYU}h>)35X*fr7F=1xn3p?ZU!uun>FqqnbBt2dIT-G@yGywpA%Hw(f literal 0 HcmV?d00001 diff --git a/Nabi/presentation/src/main/res/drawable/shape_non_day_date_picker_bg.xml b/Nabi/presentation/src/main/res/drawable/shape_non_day_date_picker_bg.xml new file mode 100644 index 0000000..24989e2 --- /dev/null +++ b/Nabi/presentation/src/main/res/drawable/shape_non_day_date_picker_bg.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/Nabi/presentation/src/main/res/layout/dialog_non_day_date_picker.xml b/Nabi/presentation/src/main/res/layout/dialog_non_day_date_picker.xml new file mode 100644 index 0000000..4c39475 --- /dev/null +++ b/Nabi/presentation/src/main/res/layout/dialog_non_day_date_picker.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/Nabi/presentation/src/main/res/layout/fragment_emotion_search.xml b/Nabi/presentation/src/main/res/layout/fragment_emotion_search.xml new file mode 100644 index 0000000..1545eca --- /dev/null +++ b/Nabi/presentation/src/main/res/layout/fragment_emotion_search.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Nabi/presentation/src/main/res/values/themes.xml b/Nabi/presentation/src/main/res/values/themes.xml index 3b5ad1e..8e4d9bf 100644 --- a/Nabi/presentation/src/main/res/values/themes.xml +++ b/Nabi/presentation/src/main/res/values/themes.xml @@ -17,4 +17,25 @@ + + + + + + + + + \ No newline at end of file