Skip to content

Commit

Permalink
Merge pull request #37 from juyoung0520/feature/#15
Browse files Browse the repository at this point in the history
Feature: #15 예약하기 화면 UI Layer 구현
  • Loading branch information
juyoung0520 authored Nov 17, 2022
2 parents 433945f + c0471f8 commit c3d9f7d
Show file tree
Hide file tree
Showing 17 changed files with 553 additions and 11 deletions.
1 change: 1 addition & 0 deletions presentation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
id 'androidx.navigation.safeargs.kotlin'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
id 'kotlin-parcelize'
}

android {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.gta.presentation.model

enum class DateType {
RANGE, DAY_COUNT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.gta.presentation.model

enum class InsuranceLevel(val price: Int) {
LOW(6310), MEDIUM(5210), HIGH(3870)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.gta.presentation.model

data class ReservationDate(val startDate: Long, val endDate: Long)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import android.widget.Button
import android.widget.TextView
import androidx.databinding.BindingAdapter
import com.gta.presentation.R
import com.gta.presentation.model.DateType
import com.gta.presentation.model.ReservationDate
import com.gta.presentation.model.carDetail.UserState
import com.gta.presentation.util.DateUtil
import java.util.*

@BindingAdapter("car_type", "car_title")
fun setCarDetailTitle(textView: TextView, type: String, title: String) {
Expand Down Expand Up @@ -32,3 +36,22 @@ fun setCarDetailBtnState(button: Button, state: UserState) {
}
}
}

@BindingAdapter("selection", "date_type")
fun setReservationTime(textView: TextView, selection: ReservationDate?, dateType: DateType) {
when (dateType) {
DateType.RANGE -> {
textView.text = selection?.let {
"${DateUtil.dateFormat.format(selection.startDate)} ~ ${DateUtil.dateFormat.format(selection.endDate)}"
} ?: textView.resources.getString(R.string.placeholder_date_range)
}
DateType.DAY_COUNT -> {
textView.text = selection?.let {
String.format(
textView.resources.getString(R.string.total_time),
DateUtil.getDateCount(it.startDate, it.endDate)
)
} ?: textView.resources.getString(R.string.placeholder_date_count)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.View
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import com.gta.presentation.R
Expand All @@ -17,6 +18,7 @@ import dagger.hilt.android.AndroidEntryPoint
class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::inflate) {
private val navHostFragment by lazy { supportFragmentManager.findFragmentById(R.id.fcv_main) as NavHostFragment }
private val navController by lazy { navHostFragment.navController }
private val appBarConfiguration = AppBarConfiguration(setOf(R.id.mapFragment, R.id.chattingFragment, R.id.myPageFragment))

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -25,13 +27,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl

setupWithBottomNavigation()

setupWithAppBar()

setupWithNaverMaps()
}

private fun setupWithAppBar() {
val appBarConfiguration = AppBarConfiguration(setOf(R.id.mapFragment, R.id.chattingFragment, R.id.myPageFragment))
setupActionBarWithNavController(navController, appBarConfiguration)
}

Expand Down Expand Up @@ -65,4 +61,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
private fun hideBottomNav() {
binding.bnvMain.visibility = View.GONE
}

override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.gta.presentation.ui.reservation

import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.navArgs
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.MaterialDatePicker
import com.gta.presentation.R
import com.gta.presentation.databinding.FragmentReservationBinding
import com.gta.presentation.model.ReservationDate
import com.gta.presentation.ui.base.BaseFragment
import com.gta.presentation.util.DateValidator
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.parcelize.Parcelize
import javax.inject.Inject

@AndroidEntryPoint
class ReservationFragment :
BaseFragment<FragmentReservationBinding>(R.layout.fragment_reservation) {
private val args: ReservationFragmentArgs by navArgs()
private val carInfo by lazy { args.carInfo }

@Inject
lateinit var viewModelFactory: ReservationViewModel.AssistedFactory

private val viewModel: ReservationViewModel by viewModels {
ReservationViewModel.provideFactory(
assistedFactory = viewModelFactory,
carInfo = carInfo
)
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
super.onCreateView(inflater, container, savedInstanceState)

setupDatePicker()

return binding.run {
vm = viewModel
carInfo = this@ReservationFragment.carInfo
root
}
}

private fun setupDatePicker() {
val constraints = CalendarConstraints.Builder()
.setValidator(DateValidator(carInfo.reservationDate, null))
.setStart(carInfo.reservationDate.first)
.setEnd(carInfo.reservationDate.second)
.build()

val datePicker = MaterialDatePicker.Builder
.dateRangePicker()
.setTheme(R.style.Theme_UCMC_DatePicker)
.setCalendarConstraints(constraints)
.build()

datePicker.addOnPositiveButtonClickListener {
viewModel.setReservationDate(ReservationDate(it.first, it.second))
}

binding.ivReservationNext.setOnClickListener {
datePicker.show(childFragmentManager, null)
}
}
}

// 임시 자동차 객체
@Parcelize
data class TmpCarInfo(
val id: String,
val title: String,
val location: String,
val carType: String,
val price: Int,
val comment: String,
val images: List<String>,
val reservationDate: Pair<Long, Long>
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.gta.presentation.ui.reservation

import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gta.presentation.R
import com.gta.presentation.model.InsuranceLevel
import com.gta.presentation.model.ReservationDate
import com.gta.presentation.util.DateUtil
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject

class ReservationViewModel @AssistedInject constructor(@Assisted private val carInfo: TmpCarInfo) :
ViewModel() {
private val _reservationDate = MutableLiveData<ReservationDate>()
val reservationDate: LiveData<ReservationDate> = _reservationDate

val selectedInsuranceOption = MutableLiveData<Int>()

private val _totalPrice = MediatorLiveData<Int>()
val totalPrice: LiveData<Int> = _totalPrice

val basePrice: LiveData<Int> = Transformations.map(_reservationDate) {
DateUtil.getDateCount(it.startDate, it.endDate) * carInfo.price
}

init {
_totalPrice.addSource(basePrice) { basePrice ->
val insurancePrice = selectedInsuranceOption.value?.let { getOptionPrice(it) } ?: 0
_totalPrice.value = basePrice.plus(insurancePrice)
}

_totalPrice.addSource(selectedInsuranceOption) { option ->
val price = basePrice.value ?: 0
_totalPrice.value = price.plus(getOptionPrice(option))
}
}

private fun getOptionPrice(option: Int): Int {
return when (option) {
R.id.rg_reservation_insurance_option_1 -> {
InsuranceLevel.LOW.price
}
R.id.rg_reservation_insurance_option_2 -> {
InsuranceLevel.MEDIUM.price
}
R.id.rg_reservation_insurance_option_3 -> {
InsuranceLevel.HIGH.price
}
else -> 0
}
}

fun setReservationDate(selected: ReservationDate) {
_reservationDate.value = selected
}

@dagger.assisted.AssistedFactory
interface AssistedFactory {
fun create(carInfo: TmpCarInfo): ReservationViewModel
}

companion object {
fun provideFactory(
assistedFactory: AssistedFactory,
carInfo: TmpCarInfo
): ViewModelProvider.Factory = object : ViewModelProvider.Factory {

@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return assistedFactory.create(carInfo) as T
}
}
}
}
16 changes: 16 additions & 0 deletions presentation/src/main/java/com/gta/presentation/util/DateUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.gta.presentation.util

import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.abs

object DateUtil {
private const val DAY_TIME_UNIT = 60 * 60 * 24 * 1000L

@JvmStatic
val dateFormat = SimpleDateFormat("yy/MM/dd", Locale.getDefault())

@JvmStatic
fun getDateCount(startDate: Long, endDate: Long) =
abs(endDate - startDate).div(DAY_TIME_UNIT).plus(1).toInt()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.gta.presentation.util

import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.MaterialDatePicker
import kotlinx.parcelize.Parcelize

@Parcelize
class DateValidator(
private val reservationRange: Pair<Long, Long>,
private val inValidList: List<Pair<Long, Long>>?
) : CalendarConstraints.DateValidator {

override fun isValid(date: Long): Boolean {
return date in reservationRange.first..reservationRange.second && date >= MaterialDatePicker.todayInUtcMilliseconds() && checkInvalidList(
date
)
}

private fun checkInvalidList(date: Long): Boolean {
inValidList ?: return true

for (range in inValidList) {
if (date in range.first..range.second) {
return false
}
}
return true
}
}
11 changes: 11 additions & 0 deletions presentation/src/main/res/drawable/ic_all_arrow_right.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M10,6L8.59,7.41L13.17,12L8.59,16.59L10,18L16,12L10,6Z"
android:fillColor="#000000"
android:fillAlpha="0.87"
android:fillType="evenOdd"/>
</vector>
Loading

0 comments on commit c3d9f7d

Please sign in to comment.