diff --git a/app/src/main/res/menu/nav_items.xml b/app/src/main/res/menu/nav_items.xml index 4f46f11..da99015 100644 --- a/app/src/main/res/menu/nav_items.xml +++ b/app/src/main/res/menu/nav_items.xml @@ -7,15 +7,16 @@ android:id="@id/personal_nav_graph" android:icon="@drawable/baseline_person_outline_24" android:title="@string/personal_nav_title" - app:showAsAction="ifRoom"/> + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index eb7a2fb..4a39921 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -42,6 +42,9 @@ object Versions { const val RETROFIT = "2.9.0" const val OKHTTP = "4.10.0" const val SERIALIZATION_CONVERTER = "0.8.0" + + // DataStore + const val DATASTORE = "1.0.0" } object Kotlin { @@ -72,6 +75,9 @@ object AndroidX { // Pagging const val PAGING3 = "androidx.paging:paging-runtime:${Versions.PAGING3}" + + // DataStore + const val DATASTORE = "androidx.datastore:datastore-preferences:${Versions.DATASTORE}" } object Material { diff --git a/common/build.gradle.kts b/common/build.gradle.kts index b3ae68a..06c5b56 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -59,4 +59,7 @@ dependencies { // DI implementation(DI.HILT) kapt(DI.HILT_COMPLIER) + + // DataStore + implementation(AndroidX.DATASTORE) } diff --git a/common/src/main/java/com/konkuk/common/data/UserRepository.kt b/common/src/main/java/com/konkuk/common/data/UserRepository.kt new file mode 100644 index 0000000..1df5e35 --- /dev/null +++ b/common/src/main/java/com/konkuk/common/data/UserRepository.kt @@ -0,0 +1,61 @@ +package com.konkuk.common.data + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class UserRepository(private val context: Context) { + + private val Context.dataStore: DataStore by preferencesDataStore(name = DATASTORE_NAME) + + private val nameKey = stringPreferencesKey(NAME_KEY) + private val ageKey = intPreferencesKey(AGE_KEY) + private val genderKey = booleanPreferencesKey(GENDER_KEY) + + val nameFlow: Flow = context.dataStore.data + .map { preferences -> + preferences[nameKey] + } + + suspend fun setName(name: String) { + context.dataStore.edit { settings -> + settings[nameKey] = name + } + } + + val ageFlow: Flow = context.dataStore.data + .map { preferences -> + preferences[ageKey] + } + + suspend fun setAge(age: Int) { + context.dataStore.edit { settings -> + settings[ageKey] = age + } + } + + val genderFlow: Flow = context.dataStore.data + .map { preferences -> + preferences[genderKey] + } + + suspend fun setGender(gender: Boolean) { + context.dataStore.edit { settings -> + settings[genderKey] = gender + } + } + + companion object { + const val DATASTORE_NAME = "settings" + const val NAME_KEY = "nameKey" + const val AGE_KEY = "ageKey" + const val GENDER_KEY = "genderKey" + } +} diff --git a/common/src/main/java/com/konkuk/common/di/DatabaseModule.kt b/common/src/main/java/com/konkuk/common/di/DatabaseModule.kt index 000d497..b48b54a 100644 --- a/common/src/main/java/com/konkuk/common/di/DatabaseModule.kt +++ b/common/src/main/java/com/konkuk/common/di/DatabaseModule.kt @@ -4,6 +4,7 @@ import android.content.Context import androidx.room.Room import com.konkuk.common.data.AppDatabase import com.konkuk.common.data.FoodInfoDao +import com.konkuk.common.data.UserRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -29,4 +30,10 @@ class DatabaseModule { fun provideFoodInfoDao(db: AppDatabase): FoodInfoDao { return db.foodInfoDao() } + + @Provides + @Singleton + fun provideUserRepository(@ApplicationContext context: Context): UserRepository { + return UserRepository(context) + } } diff --git a/common/src/main/res/color/button_color_selector.xml b/common/src/main/res/color/button_color_selector.xml new file mode 100644 index 0000000..ad05c67 --- /dev/null +++ b/common/src/main/res/color/button_color_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/common/src/main/res/drawable/baseline_female_24.xml b/common/src/main/res/drawable/baseline_female_24.xml new file mode 100644 index 0000000..9da0ba0 --- /dev/null +++ b/common/src/main/res/drawable/baseline_female_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/common/src/main/res/drawable/baseline_keyboard_arrow_right_24.xml b/common/src/main/res/drawable/baseline_keyboard_arrow_right_24.xml new file mode 100644 index 0000000..e011dbc --- /dev/null +++ b/common/src/main/res/drawable/baseline_keyboard_arrow_right_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/common/src/main/res/drawable/baseline_male_24.xml b/common/src/main/res/drawable/baseline_male_24.xml new file mode 100644 index 0000000..4d49856 --- /dev/null +++ b/common/src/main/res/drawable/baseline_male_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/common/src/main/res/drawable/btn_customfull.xml b/common/src/main/res/drawable/btn_customfull.xml new file mode 100644 index 0000000..3d3f07f --- /dev/null +++ b/common/src/main/res/drawable/btn_customfull.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/common/src/main/res/drawable/camera_pressed.xml b/common/src/main/res/drawable/camera_pressed.xml new file mode 100644 index 0000000..953572a --- /dev/null +++ b/common/src/main/res/drawable/camera_pressed.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/common/src/main/res/drawable/camera_unpressed.xml b/common/src/main/res/drawable/camera_unpressed.xml new file mode 100644 index 0000000..9ac8cc5 --- /dev/null +++ b/common/src/main/res/drawable/camera_unpressed.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/common/src/main/res/drawable/female_pressed.xml b/common/src/main/res/drawable/female_pressed.xml new file mode 100644 index 0000000..78a6d4f --- /dev/null +++ b/common/src/main/res/drawable/female_pressed.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/common/src/main/res/drawable/fragment_bottom_sheet_enroll.xml b/common/src/main/res/drawable/fragment_bottom_sheet_enroll.xml new file mode 100644 index 0000000..c1b5d9d --- /dev/null +++ b/common/src/main/res/drawable/fragment_bottom_sheet_enroll.xml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/src/main/res/drawable/outline_calendar_month_24.xml b/common/src/main/res/drawable/outline_calendar_month_24.xml new file mode 100644 index 0000000..252b5d2 --- /dev/null +++ b/common/src/main/res/drawable/outline_calendar_month_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/common/src/main/res/drawable/rectangle_roundfull.xml b/common/src/main/res/drawable/rectangle_roundfull.xml new file mode 100644 index 0000000..58b9af3 --- /dev/null +++ b/common/src/main/res/drawable/rectangle_roundfull.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/common/src/main/res/drawable/rectanglesmall4x.png b/common/src/main/res/drawable/rectanglesmall4x.png new file mode 100644 index 0000000..4c00a72 Binary files /dev/null and b/common/src/main/res/drawable/rectanglesmall4x.png differ diff --git a/common/src/main/res/drawable/selector_female.xml b/common/src/main/res/drawable/selector_female.xml new file mode 100644 index 0000000..8403d6e --- /dev/null +++ b/common/src/main/res/drawable/selector_female.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/common/src/main/res/drawable/selector_male.xml b/common/src/main/res/drawable/selector_male.xml new file mode 100644 index 0000000..83da73f --- /dev/null +++ b/common/src/main/res/drawable/selector_male.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/feat/capture/src/main/java/com/konkuk/capture/ui/capture/CaptureActivity.kt b/feat/capture/src/main/java/com/konkuk/capture/ui/capture/CaptureActivity.kt index 180ca69..17e122d 100644 --- a/feat/capture/src/main/java/com/konkuk/capture/ui/capture/CaptureActivity.kt +++ b/feat/capture/src/main/java/com/konkuk/capture/ui/capture/CaptureActivity.kt @@ -3,6 +3,7 @@ package com.konkuk.capture.ui.capture import android.annotation.SuppressLint import android.content.Intent import android.graphics.Bitmap +import android.net.Uri import android.os.Bundle import android.provider.MediaStore import androidx.activity.result.contract.ActivityResultContracts @@ -13,7 +14,9 @@ import com.google.mlkit.vision.text.TextRecognition import com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions import com.konkuk.capture.databinding.ActivityCaptureBinding import com.konkuk.capture.ui.enroll.EnrollTextInput +import com.konkuk.capture.ui.enroll.EnrollTextInputViewModel.Companion.BITMAP_PICTURE_KEY import com.konkuk.capture.ui.enroll.EnrollTextInputViewModel.Companion.OCR_RESULT_KEY +import com.konkuk.capture.ui.enroll.EnrollTextInputViewModel.Companion.URI_PICTURE_KEY class CaptureActivity : AppCompatActivity() { @@ -22,11 +25,15 @@ class CaptureActivity : AppCompatActivity() { private val recognizer = TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build()) + private var bitmapPicture: Bitmap? = null + private var uriPicture: Uri? = null + private val takePictureLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult(), ) { if (it.resultCode == RESULT_OK) { (it.data?.extras?.get("data") as Bitmap?)?.let { bitmap -> + bitmapPicture = bitmap binding.ivNutritionInfo.setImageBitmap(bitmap) processImageRecognize(InputImage.fromBitmap(bitmap, 0)) } ?: reCapture() @@ -40,6 +47,7 @@ class CaptureActivity : AppCompatActivity() { ) { if (it.resultCode == RESULT_OK) { it.data?.data?.let { uri -> + uriPicture = uri binding.ivNutritionInfo.setImageURI(uri) processImageRecognize(InputImage.fromFilePath(this@CaptureActivity, uri)) } ?: reCapture() @@ -69,6 +77,11 @@ class CaptureActivity : AppCompatActivity() { reCapture() }) captureDialog.show(supportFragmentManager, "CaptureDialogFragment") + + binding.llBackBtn.setOnClickListener { + finish() + startActivity(Intent(this@CaptureActivity, EnrollTextInput::class.java)) + } } @SuppressLint("SetTextI18n") @@ -88,10 +101,9 @@ class CaptureActivity : AppCompatActivity() { finish() startActivity( Intent(this@CaptureActivity, EnrollTextInput::class.java).apply { - putExtra( - OCR_RESULT_KEY, - visionText.text, - ) + putExtra(OCR_RESULT_KEY, visionText.text) + putExtra(BITMAP_PICTURE_KEY, bitmapPicture) + putExtra(URI_PICTURE_KEY, uriPicture) }, ) } diff --git a/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollBindingAdapter.kt b/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollBindingAdapter.kt new file mode 100644 index 0000000..abacd8f --- /dev/null +++ b/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollBindingAdapter.kt @@ -0,0 +1,14 @@ +package com.konkuk.capture.ui.enroll + +import android.widget.ImageView +import androidx.databinding.BindingAdapter + +@BindingAdapter("capturedPicture") +fun ImageView.capturedPicture(capturedPicture: CapturedPicture) { + println("capturedPicture $capturedPicture") + when (capturedPicture) { + is CapturedPicture.BitmapPicture -> setImageBitmap(capturedPicture.bitmap) + is CapturedPicture.UriPicture -> setImageURI(capturedPicture.uri) + else -> {} + } +} diff --git a/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollBottomSheetDialogFragment.kt b/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollBottomSheetDialogFragment.kt index 342d310..deff66d 100644 --- a/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollBottomSheetDialogFragment.kt +++ b/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollBottomSheetDialogFragment.kt @@ -5,16 +5,24 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.konkuk.capture.databinding.FragmentBottomSheetEnrollBinding import com.konkuk.capture.ui.capture.CaptureActivity import com.konkuk.capture.ui.search.SearchFoodActivity +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch class EnrollBottomSheetDialogFragment : BottomSheetDialogFragment() { private var _binding: FragmentBottomSheetEnrollBinding? = null private val binding: FragmentBottomSheetEnrollBinding get() = requireNotNull(_binding) + private val selection = MutableStateFlow(null) + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -27,35 +35,41 @@ class EnrollBottomSheetDialogFragment : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.lifecycleOwner = viewLifecycleOwner - initSelector() - initNext() } - private fun initNext() { - binding.btnContinue.setOnClickListener { - dismiss() - if (binding.selectorCamera.isSelected) { + private fun initSelector() = with(binding) { + clickLeft = { + selection.value = true + } + + clickRight = { + selection.value = false + } + + btnContinue.setOnClickListener { + if (selection.value == true) { val intent = Intent(context, CaptureActivity::class.java) startActivity(intent) - } else { + } else if (selection.value == false) { val intent = Intent(context, SearchFoodActivity::class.java) startActivity(intent) } + dismiss() } - } - private fun initSelector() = with(binding) { - selectorCamera.isSelected = true - selectorPen.isSelected = false - selectorCamera.setOnClickListener { - selectorCamera.isSelected = true - selectorPen.isSelected = false - } - selectorPen.setOnClickListener { - selectorCamera.isSelected = false - selectorPen.isSelected = true + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + selection.collectLatest { selected -> + selected?.let { + btnContinue.isEnabled = true + selectorCamera.isSelected = it + selectorPen.isSelected = it.not() + } ?: kotlin.run { + btnContinue.isEnabled = false + } + } + } } } diff --git a/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollTextInputViewModel.kt b/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollTextInputViewModel.kt index fd50119..5a9f636 100644 --- a/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollTextInputViewModel.kt +++ b/feat/capture/src/main/java/com/konkuk/capture/ui/enroll/EnrollTextInputViewModel.kt @@ -1,5 +1,7 @@ package com.konkuk.capture.ui.enroll +import android.graphics.Bitmap +import android.net.Uri import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import com.konkuk.common.data.FoodInfo @@ -34,6 +36,8 @@ class EnrollTextInputViewModel @Inject constructor( val saturatedFat = MutableStateFlow("") val calories = MutableStateFlow("") + val capturedPicture = MutableStateFlow(CapturedPicture.None) + init { savesStateHandle.get(OCR_RESULT_KEY)?.let { text -> val result = text.replace(" ", "") @@ -45,6 +49,11 @@ class EnrollTextInputViewModel @Inject constructor( savesStateHandle.get(API_RESULT_KEY)?.let { setFoodInfo(it) } + savesStateHandle.get(URI_PICTURE_KEY)?.let { + capturedPicture.value = CapturedPicture.UriPicture(it) + } ?: savesStateHandle.get(BITMAP_PICTURE_KEY)?.let { + capturedPicture.value = CapturedPicture.BitmapPicture(it) + } } private fun setFoodInfo(foodInfo: FoodInfo) { @@ -112,5 +121,13 @@ class EnrollTextInputViewModel @Inject constructor( companion object { const val OCR_RESULT_KEY = "OCR_RESULT_KEY" const val API_RESULT_KEY = "API_RESULT_KEY" + const val BITMAP_PICTURE_KEY = "BITMAP_PICTURE_KEY" + const val URI_PICTURE_KEY = "URI_PICTURE_KEY" } } + +sealed class CapturedPicture { + data class UriPicture(val uri: Uri) : CapturedPicture() + data class BitmapPicture(val bitmap: Bitmap) : CapturedPicture() + object None : CapturedPicture() +} diff --git a/feat/capture/src/main/java/com/konkuk/capture/ui/search/SearchFoodActivity.kt b/feat/capture/src/main/java/com/konkuk/capture/ui/search/SearchFoodActivity.kt index 231ac1a..b1d630f 100644 --- a/feat/capture/src/main/java/com/konkuk/capture/ui/search/SearchFoodActivity.kt +++ b/feat/capture/src/main/java/com/konkuk/capture/ui/search/SearchFoodActivity.kt @@ -2,6 +2,7 @@ package com.konkuk.capture.ui.search import android.content.Intent import android.os.Bundle +import android.view.View import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Lifecycle @@ -16,6 +17,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import javax.inject.Inject @@ -59,6 +61,17 @@ class SearchFoodActivity : AppCompatActivity() { } } + lifecycleScope.launch { + viewModel.textField.collectLatest { text -> + if (text.isBlank()) { + delay(200) + binding.llSearchNotiImage.visibility = View.VISIBLE + } else { + binding.llSearchNotiImage.visibility = View.GONE + } + } + } + lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.event.collectLatest { diff --git a/feat/capture/src/main/res/layout/activity_capture.xml b/feat/capture/src/main/res/layout/activity_capture.xml index d8c2a9d..2110446 100644 --- a/feat/capture/src/main/res/layout/activity_capture.xml +++ b/feat/capture/src/main/res/layout/activity_capture.xml @@ -6,20 +6,46 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/stroke_deepblue" + android:background="@color/white" tools:context=".ui.capture.CaptureActivity"> + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/ll_back_btn" /> + - - + + - - + + + + + + - - + \ No newline at end of file diff --git a/feat/capture/src/main/res/layout/activity_enroll_text_input.xml b/feat/capture/src/main/res/layout/activity_enroll_text_input.xml index 1f2fc2e..7750bfa 100644 --- a/feat/capture/src/main/res/layout/activity_enroll_text_input.xml +++ b/feat/capture/src/main/res/layout/activity_enroll_text_input.xml @@ -43,7 +43,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="11dp" - android:text="직접 입력" + android:text="뒤로 가기" android:textColor="@color/black" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" @@ -60,9 +60,16 @@ + + - + app:layout_constraintTop_toTopOf="@id/btn_back" + app:strokeColor="@color/main_blue" + app:strokeWidth="1dp"> + + + + + + + + + + + + - + app:layout_constraintTop_toBottomOf="@id/cv_search" /> + + + + + + + + + + + + + + + + + + + + + @@ -60,6 +73,7 @@ android:id="@+id/selectorCamera" android:layout_width="72dp" android:layout_height="72dp" + android:onClick="@{() -> clickLeft.invoke()}" android:background="@drawable/selector_camera" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -104,6 +118,7 @@ > { + operator fun invoke(): Result>> { return kotlin.runCatching { flow { - emit(Calendar.getInstance().get(Calendar.DAY_OF_MONTH)) + emit( + Triple( + Calendar.getInstance().get(Calendar.YEAR), + Calendar.getInstance().get(Calendar.MONTH) + 1, + Calendar.getInstance().get(Calendar.DAY_OF_MONTH), + ), + ) } } } diff --git a/feat/history/src/main/java/com/konkuk/history/domain/usecase/GetHistoryListUseCase.kt b/feat/history/src/main/java/com/konkuk/history/domain/usecase/GetHistoryListUseCase.kt index 9ff2b17..b002828 100644 --- a/feat/history/src/main/java/com/konkuk/history/domain/usecase/GetHistoryListUseCase.kt +++ b/feat/history/src/main/java/com/konkuk/history/domain/usecase/GetHistoryListUseCase.kt @@ -9,8 +9,16 @@ import javax.inject.Inject class GetHistoryListUseCase @Inject constructor( private val historyRepository: HistoryRepository, ) { - operator fun invoke(selectedDay: Int): Result>> { + operator fun invoke( + year: Int? = null, + month: Int? = null, + selectedDay: Int, + ): Result>> { val date = Date(System.currentTimeMillis()) - return historyRepository.getFoodHistory(date.year + 1900, date.month + 1, selectedDay) + return historyRepository.getFoodHistory( + year ?: (date.year + 1900), + month ?: (date.month + 1), + selectedDay, + ) } } diff --git a/feat/history/src/main/java/com/konkuk/history/ui/history/DatePickerFragment.kt b/feat/history/src/main/java/com/konkuk/history/ui/history/DatePickerFragment.kt new file mode 100644 index 0000000..e384ffd --- /dev/null +++ b/feat/history/src/main/java/com/konkuk/history/ui/history/DatePickerFragment.kt @@ -0,0 +1,28 @@ +package com.konkuk.history.ui.history + +import android.app.DatePickerDialog +import android.app.DatePickerDialog.OnDateSetListener +import android.app.Dialog +import android.os.Bundle +import android.widget.DatePicker +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint +import java.util.Calendar + +@AndroidEntryPoint +class DatePickerFragment(private val onPicked: (year: Int, month: Int, day: Int) -> Unit) : DialogFragment(), OnDateSetListener { + + private val viewModel by viewModels() + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val c: Calendar = Calendar.getInstance() + val year: Int = c.get(Calendar.YEAR) + val month: Int = c.get(Calendar.MONTH) + val day: Int = c.get(Calendar.DAY_OF_MONTH) + return DatePickerDialog(requireContext(), this, year, month, day) + } + + override fun onDateSet(datePicker: DatePicker, year: Int, month: Int, day: Int) { + onPicked(year, month + 1, day) + } +} diff --git a/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryDateUiState.kt b/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryDateUiState.kt index ae214b7..e02d6da 100644 --- a/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryDateUiState.kt +++ b/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryDateUiState.kt @@ -5,5 +5,5 @@ import com.konkuk.history.domain.model.HistoryCalendarModel sealed class HistoryDateUiState { object Uninitialized : HistoryDateUiState() data class Error(val message: String) : HistoryDateUiState() - data class Avail(val today: Int, val selectedDay: Int, val calendarList: List) : HistoryDateUiState() + data class Avail(val year: Int, val month: Int, val today: Int, val selectedDay: Int, val calendarList: List) : HistoryDateUiState() } diff --git a/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryFragment.kt b/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryFragment.kt index 48aca0d..3a26dd9 100644 --- a/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryFragment.kt +++ b/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryFragment.kt @@ -60,6 +60,12 @@ class HistoryFragment : Fragment() { val intent = Intent(requireContext(), HistoryStatisticsActivity::class.java).putExtra( StatisticViewModel.SELECTED_DAY_KEY, (viewModel.uiState.value.historyDateUiState as HistoryDateUiState.Avail).selectedDay, + ).putExtra( + StatisticViewModel.SELECTED_MONTH_KEY, + (viewModel.uiState.value.historyDateUiState as HistoryDateUiState.Avail).month, + ).putExtra( + StatisticViewModel.SELECTED_YEAR_KEY, + (viewModel.uiState.value.historyDateUiState as HistoryDateUiState.Avail).year, ) startActivity(intent) } @@ -102,6 +108,11 @@ class HistoryFragment : Fragment() { } private fun initViews() = with(binding) { + ivCalendarButton.setOnClickListener { + DatePickerFragment { year, month, day -> + viewModel.setDate(year, month, day) + }.show(parentFragmentManager, "datePicker") + } } private fun observeUiState() { @@ -120,6 +131,7 @@ class HistoryFragment : Fragment() { is HistoryDateUiState.Uninitialized -> {} is HistoryDateUiState.Error -> {} is HistoryDateUiState.Avail -> { + tvHistoryTitle.text = "${historyDateUiState.month}월의 기록" tvProgressTitle.text = "${historyDateUiState.selectedDay}일의 분석결과 보러가기" calendarAdapter.submitList(historyDateUiState.calendarList.toList()) } diff --git a/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryViewModel.kt b/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryViewModel.kt index 4ce59a5..f278217 100644 --- a/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryViewModel.kt +++ b/feat/history/src/main/java/com/konkuk/history/ui/history/HistoryViewModel.kt @@ -35,8 +35,13 @@ class HistoryViewModel @Inject constructor( initFoodHistory() } - private fun initCalendar(): List { + private fun initCalendar( + selectedDate: Triple? = null, + ): List { val calendar = Calendar.getInstance() + selectedDate?.let { (year, month, day) -> + calendar.set(year, month - 1, day) + } val today = calendar.get(Calendar.DAY_OF_MONTH) calendar.set(Calendar.DAY_OF_MONTH, 1) // 이번 달의 1일로 설정 @@ -56,14 +61,33 @@ class HistoryViewModel @Inject constructor( return calendarList } + fun setDate(year: Int, month: Int, day: Int) { + _uiState.value = _uiState.value.copy( + historyDateUiState = HistoryDateUiState.Avail( + year, + month, + day, + day, + initCalendar(Triple(year, month, day)), + ), + ) + initHistoryList(year, month, day) + } + private fun initFoodHistory() { getHistoryDateUseCase().onSuccess { value -> viewModelScope.launch { - val today = value.first() + val (year, month, today) = value.first() _uiState.value = _uiState.value.copy( - historyDateUiState = HistoryDateUiState.Avail(today, today, initCalendar()), + historyDateUiState = HistoryDateUiState.Avail( + year, + month, + today, + today, + initCalendar(), + ), ) - initHistoryList(today) + initHistoryList(selectedDay = today) } }.onFailure { _uiState.value = _uiState.value.copy( @@ -73,8 +97,8 @@ class HistoryViewModel @Inject constructor( } } - private fun initHistoryList(selectedDay: Int) { - getHistoryListUseCase(selectedDay).onSuccess { value -> + private fun initHistoryList(year: Int? = null, month: Int? = null, selectedDay: Int) { + getHistoryListUseCase(year, month, selectedDay).onSuccess { value -> job?.cancel() job = Job() CoroutineScope((job ?: Job())).launch { @@ -106,12 +130,17 @@ class HistoryViewModel @Inject constructor( }, ), ) - getHistoryList(selectedDay) + + getHistoryList( + (_uiState.value.historyDateUiState as HistoryDateUiState.Avail).year, + (_uiState.value.historyDateUiState as HistoryDateUiState.Avail).month, + selectedDay, + ) } } - private fun getHistoryList(selectedDay: Int) { - getHistoryListUseCase(selectedDay).onSuccess { value -> + private fun getHistoryList(year: Int, month: Int, selectedDay: Int) { + getHistoryListUseCase(year, month, selectedDay).onSuccess { value -> job?.cancel() job = Job() CoroutineScope((job ?: Job())).launch { diff --git a/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/HistoryStatisticsActivity.kt b/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/HistoryStatisticsActivity.kt index 86910bf..91f12cb 100644 --- a/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/HistoryStatisticsActivity.kt +++ b/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/HistoryStatisticsActivity.kt @@ -2,11 +2,9 @@ package com.konkuk.history.ui.history.statistic import android.os.Bundle import androidx.activity.viewModels -import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import com.konkuk.history.databinding.ActivityHistoryStatisticsBinding -import com.konkuk.history.databinding.DialogAgeInputBinding import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -44,29 +42,5 @@ class HistoryStatisticsActivity : AppCompatActivity() { btnBack.setOnClickListener { finish() } - - btnAge.setOnClickListener { - selectAge() - } - } - - private fun selectAge() { - val dialogBinding = DialogAgeInputBinding.inflate(layoutInflater) - - AlertDialog.Builder(this@HistoryStatisticsActivity) - .setView( - dialogBinding.root, - ) - .setPositiveButton( - "확인", - ) { dialog, _ -> - val age = dialogBinding.etAge.text.toString().toIntOrNull() ?: 0 - if (age > 0) viewModel.changeAge(age) - dialog.dismiss() - } - .setNegativeButton( - "취소", - ) { dialog, _ -> dialog.dismiss() } - .show() } } diff --git a/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/StaticsBindingAdapter.kt b/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/StaticsBindingAdapter.kt index ea63f2b..ae9d77b 100644 --- a/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/StaticsBindingAdapter.kt +++ b/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/StaticsBindingAdapter.kt @@ -6,11 +6,13 @@ import com.konkuk.history.data.datasource.statistic.NutritionStat @BindingAdapter("gender") fun TextView.gender(gender: GENDER) { - this.text = if (gender == GENDER.MALE) "남성" else "여성" + this.text = if (gender == GENDER.NONE) "" else if (gender == GENDER.MALE) "남성" else "여성" } @BindingAdapter("ageRange") fun TextView.ageRange(age: Int) { - val range = NutritionStat.ageList[NutritionStat.getAgeIndex(age)] - this.text = "${range.first} ~ ${range.last}살" + if (age != 0) { + val range = NutritionStat.ageList[NutritionStat.getAgeIndex(age)] + this.text = "${range.first} ~ ${range.last}살" + } } diff --git a/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/StatisticViewModel.kt b/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/StatisticViewModel.kt index 7f93f4a..157f92f 100644 --- a/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/StatisticViewModel.kt +++ b/feat/history/src/main/java/com/konkuk/history/ui/history/statistic/StatisticViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.konkuk.common.R +import com.konkuk.common.data.UserRepository import com.konkuk.history.data.datasource.statistic.NutritionStat import com.konkuk.history.data.datasource.statistic.StatCSVParser import com.konkuk.history.domain.model.HistoryItemModel @@ -22,6 +23,7 @@ class StatisticViewModel @Inject constructor( private val getHistoryListUseCase: GetHistoryListUseCase, private val getMonthUseCase: GetMonthUseCase, private val parser: StatCSVParser, + private val userRepository: UserRepository, savedStateHandle: SavedStateHandle, ) : ViewModel() { private val statList = MutableStateFlow(NutritionStat(0, 0, 0, 0, 0, 0)) @@ -33,18 +35,28 @@ class StatisticViewModel @Inject constructor( val date = MutableStateFlow("-월 -일의 분석") - private val _gender = MutableStateFlow(GENDER.MALE) + private val _gender = MutableStateFlow(GENDER.NONE) val gender get() = _gender.asStateFlow() - private val _age = MutableStateFlow(18) + private val _age = MutableStateFlow(0) val age get() = _age.asStateFlow() val dataList = MutableStateFlow>(emptyList()) init { - initMyStat(savedStateHandle.get(SELECTED_DAY_KEY)!!) + initMyStat( + savedStateHandle.get(SELECTED_YEAR_KEY)!!, + savedStateHandle.get(SELECTED_MONTH_KEY)!!, + savedStateHandle.get(SELECTED_DAY_KEY)!!, + ) initAvgStat() initDateList() + + viewModelScope.launch { + _gender.value = + if (userRepository.genderFlow.first() == true) GENDER.MALE else GENDER.FEMALE + _age.value = userRepository.ageFlow.first()?.let { it } ?: 23 + } } private fun initDateList() { @@ -131,11 +143,11 @@ class StatisticViewModel @Inject constructor( } } - private fun initMyStat(selectedDay: Int?) { + private fun initMyStat(year: Int, month: Int, selectedDay: Int) { date.value = "${getMonthUseCase()}월 ${selectedDay}일의 분석" viewModelScope.launch { val historyList = - getHistoryListUseCase(requireNotNull(selectedDay)).getOrNull()?.first() + getHistoryListUseCase(year, month, selectedDay).getOrNull()?.first() if (historyList?.size == 0) return@launch nutList.value = historyList!!.reduce { item, sum -> HistoryItemModel( @@ -153,19 +165,13 @@ class StatisticViewModel @Inject constructor( } } - fun changeAge(age: Int) { - if (age > 0) _age.value = age - } - - fun changeGender() { - _gender.value = if (gender.value == GENDER.MALE) GENDER.FEMALE else GENDER.MALE - } - companion object { const val SELECTED_DAY_KEY = "SELECTED_DAY_KEY" + const val SELECTED_MONTH_KEY = "SELECTED_MONTH_KEY" + const val SELECTED_YEAR_KEY = "SELECTED_YEAR_KEY" } } enum class GENDER { - MALE, FEMALE + MALE, FEMALE, NONE } diff --git a/feat/history/src/main/res/layout/activity_history_statistics.xml b/feat/history/src/main/res/layout/activity_history_statistics.xml index 18d99eb..4b52573 100644 --- a/feat/history/src/main/res/layout/activity_history_statistics.xml +++ b/feat/history/src/main/res/layout/activity_history_statistics.xml @@ -73,7 +73,7 @@ android:paddingHorizontal="25dp"> - + android:orientation="horizontal"> + + + + + + + + + + + + + + \ No newline at end of file diff --git a/feat/personal/src/main/java/com/konkuk/personal/ui/personal/PersonalFragment.kt b/feat/personal/src/main/java/com/konkuk/personal/ui/personal/PersonalFragment.kt index 95f4f90..65f6cc1 100644 --- a/feat/personal/src/main/java/com/konkuk/personal/ui/personal/PersonalFragment.kt +++ b/feat/personal/src/main/java/com/konkuk/personal/ui/personal/PersonalFragment.kt @@ -1,6 +1,7 @@ package com.konkuk.personal.ui.personal import android.annotation.SuppressLint +import android.content.Intent import android.graphics.Color import android.os.Bundle import android.util.Log @@ -24,6 +25,7 @@ import com.github.mikephil.charting.data.LineData import com.github.mikephil.charting.data.LineDataSet import com.konkuk.common.ui.decoration.AnimateProgressBarCommon import com.konkuk.personal.databinding.FragmentPersonalBinding +import com.konkuk.personal.ui.settings.SettingsActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -50,6 +52,13 @@ class PersonalFragment : Fragment() { binding.lifecycleOwner = viewLifecycleOwner observeUiState() + initViews() + } + + private fun initViews() { + binding.ivSettings.setOnClickListener { + startActivity(Intent(requireContext(), SettingsActivity::class.java)) + } } private fun observeUiState() { diff --git a/feat/personal/src/main/java/com/konkuk/personal/ui/settings/EditAgeActivity.kt b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/EditAgeActivity.kt new file mode 100644 index 0000000..443eae1 --- /dev/null +++ b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/EditAgeActivity.kt @@ -0,0 +1,37 @@ +package com.konkuk.personal.ui.settings + +import android.os.Bundle +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import com.konkuk.personal.databinding.ActivityEditAgeBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class EditAgeActivity : AppCompatActivity() { + + private lateinit var binding: ActivityEditAgeBinding + private val viewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityEditAgeBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.lifecycleOwner = this + binding.vm = viewModel + + initViews() + } + + private fun initViews() = with(binding) { + ivBackBtn.setOnClickListener { + finish() + } + + tvSave.setOnClickListener { + viewModel.setAge() + finish() + } + } +} diff --git a/feat/personal/src/main/java/com/konkuk/personal/ui/settings/EditGenderBottomSheetDialogFragment.kt b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/EditGenderBottomSheetDialogFragment.kt new file mode 100644 index 0000000..0a6e56a --- /dev/null +++ b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/EditGenderBottomSheetDialogFragment.kt @@ -0,0 +1,67 @@ +package com.konkuk.personal.ui.settings + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.konkuk.personal.databinding.FragmentEditGenderBinding +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch + +@AndroidEntryPoint +class EditGenderBottomSheetDialogFragment : BottomSheetDialogFragment() { + + private var _binding: FragmentEditGenderBinding? = null + private val binding: FragmentEditGenderBinding get() = requireNotNull(_binding) + + private val viewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentEditGenderBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.lifecycleOwner = viewLifecycleOwner + binding.vm = viewModel + + initSelector() + initNext() + } + + private fun initNext() { + binding.btnContinue.setOnClickListener { + viewModel.setGender() + dismiss() + } + } + + private fun initSelector() = with(binding) { + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.editableGender.collect { isMale -> + isMale?.let { + selectorMale.isSelected = it + selectorFemale.isSelected = it.not() + } + } + } + } + } + + override fun onDestroy() { + _binding = null + super.onDestroy() + } +} diff --git a/feat/personal/src/main/java/com/konkuk/personal/ui/settings/EditNameActivity.kt b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/EditNameActivity.kt new file mode 100644 index 0000000..882d933 --- /dev/null +++ b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/EditNameActivity.kt @@ -0,0 +1,37 @@ +package com.konkuk.personal.ui.settings + +import android.os.Bundle +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import com.konkuk.personal.databinding.ActivityEditNameBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class EditNameActivity : AppCompatActivity() { + + private lateinit var binding: ActivityEditNameBinding + private val viewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityEditNameBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.lifecycleOwner = this + binding.vm = viewModel + + initViews() + } + + private fun initViews() = with(binding) { + ivBackBtn.setOnClickListener { + finish() + } + + tvSave.setOnClickListener { + viewModel.setName() + finish() + } + } +} diff --git a/feat/personal/src/main/java/com/konkuk/personal/ui/settings/SettingsActivity.kt b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/SettingsActivity.kt new file mode 100644 index 0000000..621e5e9 --- /dev/null +++ b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/SettingsActivity.kt @@ -0,0 +1,48 @@ +package com.konkuk.personal.ui.settings + +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import com.konkuk.personal.databinding.ActivitySettingsBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class SettingsActivity : AppCompatActivity() { + + private lateinit var binding: ActivitySettingsBinding + private val viewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySettingsBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.lifecycleOwner = this + binding.vm = viewModel + + initViews() + } + + private fun initViews() = with(binding) { + llName.setOnClickListener { + startActivity(Intent(this@SettingsActivity, EditNameActivity::class.java)) + } + + llAge.setOnClickListener { + startActivity(Intent(this@SettingsActivity, EditAgeActivity::class.java)) + } + + llGender.setOnClickListener { + val bottomSheetDialogFragment = EditGenderBottomSheetDialogFragment() + bottomSheetDialogFragment.show( + supportFragmentManager, + bottomSheetDialogFragment.tag, + ) + } + + ivBackBtn.setOnClickListener { + finish() + } + } +} diff --git a/feat/personal/src/main/java/com/konkuk/personal/ui/settings/SettingsBindingAdapter.kt b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/SettingsBindingAdapter.kt new file mode 100644 index 0000000..621d1f5 --- /dev/null +++ b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/SettingsBindingAdapter.kt @@ -0,0 +1,15 @@ +package com.konkuk.personal.ui.settings + +import android.widget.TextView +import androidx.databinding.BindingAdapter + +@BindingAdapter("isEnabled") +fun TextView.isEnabled(enable: Boolean) { + if (enable) { + setBackgroundColor(resources.getColor(com.konkuk.common.R.color.main_blue)) + this.isEnabled = true + } else { + setBackgroundColor(resources.getColor(com.konkuk.common.R.color.static_gray)) + this.isEnabled = false + } +} diff --git a/feat/personal/src/main/java/com/konkuk/personal/ui/settings/SettingsViewModel.kt b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/SettingsViewModel.kt new file mode 100644 index 0000000..d889c08 --- /dev/null +++ b/feat/personal/src/main/java/com/konkuk/personal/ui/settings/SettingsViewModel.kt @@ -0,0 +1,84 @@ +package com.konkuk.personal.ui.settings + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.konkuk.common.data.UserRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class SettingsViewModel @Inject constructor(private val userRepository: UserRepository) : + ViewModel() { + val name = + userRepository.nameFlow + .map { + if (it == null) "이름을 입력해주세요" else it + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "") + val age = + userRepository.ageFlow.map { + if (it == null) "나이을 입력해주세요" else it.toString() + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "") + val gender = + userRepository.genderFlow.map { + if (it == null) "성별을 입력해주세요" else if (it) "남자" else "여자" + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "") + + val editableName = MutableStateFlow("") + val editableAge = MutableStateFlow("") + val editableGender = MutableStateFlow(null) + + val canEditName = userRepository.nameFlow.combine(editableName) { savedName, editName -> + savedName ?: "" != editName + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) + + val canEditAge = userRepository.ageFlow.combine(editableAge) { savedAge, editAge -> + savedAge.toString() != editAge && editAge.toIntOrNull() != null + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) + + init { + viewModelScope.launch { + editableName.value = userRepository.nameFlow.first() ?: "" + editableAge.value = userRepository.ageFlow.first()?.let { + it.toString() + } ?: "" + editableGender.value = userRepository.genderFlow.first() + } + } + + fun setName() { + viewModelScope.launch { + userRepository.setName(editableName.value) + } + } + + fun setAge() { + editableAge.value.toIntOrNull()?.let { + viewModelScope.launch { + userRepository.setAge(it) + } + } + } + + fun setGender() { + editableGender.value?.let { + viewModelScope.launch { + userRepository.setGender(it) + } + } + } + + fun selectMale() { + editableGender.value = true + } + + fun selectFemale() { + editableGender.value = false + } +} diff --git a/feat/personal/src/main/res/layout/activity_edit_age.xml b/feat/personal/src/main/res/layout/activity_edit_age.xml new file mode 100644 index 0000000..d4b9998 --- /dev/null +++ b/feat/personal/src/main/res/layout/activity_edit_age.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/feat/personal/src/main/res/layout/activity_edit_name.xml b/feat/personal/src/main/res/layout/activity_edit_name.xml new file mode 100644 index 0000000..fccb82c --- /dev/null +++ b/feat/personal/src/main/res/layout/activity_edit_name.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/feat/personal/src/main/res/layout/activity_settings.xml b/feat/personal/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..c264cff --- /dev/null +++ b/feat/personal/src/main/res/layout/activity_settings.xml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/feat/personal/src/main/res/layout/fragment_edit_gender.xml b/feat/personal/src/main/res/layout/fragment_edit_gender.xml new file mode 100644 index 0000000..81b254d --- /dev/null +++ b/feat/personal/src/main/res/layout/fragment_edit_gender.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/feat/personal/src/main/res/layout/fragment_personal.xml b/feat/personal/src/main/res/layout/fragment_personal.xml index 373dbf0..6a7c694 100644 --- a/feat/personal/src/main/res/layout/fragment_personal.xml +++ b/feat/personal/src/main/res/layout/fragment_personal.xml @@ -17,8 +17,8 @@ + android:layout_marginHorizontal="24dp" + android:layout_marginTop="24dp"> + + +