From cde30c694852c278e5820d79a833ae4bf5cffd39 Mon Sep 17 00:00:00 2001 From: Somin Lee <46596035+s6m1n@users.noreply.github.com> Date: Thu, 25 Jul 2024 14:40:05 +0900 Subject: [PATCH] =?UTF-8?q?ui:=20=EB=B0=A9=EB=AC=B8=20=EA=B8=B0=EB=A1=9D,?= =?UTF-8?q?=20=EB=B0=A9=EB=AC=B8=20=EA=B8=B0=EB=A1=9D=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?,=20=EB=B0=A9=EB=AC=B8=20=EA=B8=B0=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84=20#52=20(#7?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ui: typography.body textSize 1sp 씩 증가 * feat: DeleteDialogFragment에 Handler 추가 * feat: 툴바의 수정, 삭제 버튼 제어를 위한 ToolbarHandler 추가 * feat: 방문 상세 화면을 위한 VisitDetailUiModel 추가 * ui: PlaceHolder를 위한 xml 파일 추가 * feat: 방문 기록 상세 화면을 위한 VisitAdapter 및 VisitViewHolder 구현 * feat: 임시 VisitViewModel와 VisitViewModelFactory 추가 * feat: VisitFragment 화면 구현 * feat: 방문 기록에 해당하는 여행 선택을 위한 TravelSelectionFragment 구현 * feat: 방문 기록에 해당하는 날짜 선택을 위한 VisitedAtSelectionFragment 구현 * feat: 방문 기록 생성을 위한 VisitCreationActivity 구현 * feat: 방문 기록 수정을 위한 VisitUpdateActivity 구현 * refactor: DialogHandler를 DeleteDialogFragment의 생성자에서 받도록 수정 * refactor: initVisitUpdateDoneButton 중복 로직 제거 * refactor: VisitViewHolderType 메서드 명 변경 of -> from * refactor: tv_place_name_title을 xml id convention에 맞게 수정 --- .../staccato/DeleteDialogFragment.kt | 17 +- .../staccato/presentation/BindingAdapters.kt | 71 +++++++ .../staccato/presentation/ToolbarHandler.kt | 7 + .../presentation/visit/VisitFragment.kt | 59 +++++- .../visit/adapter/VisitAdapter.kt | 76 +++++++ .../visit/adapter/VisitViewHolder.kt | 23 +++ .../visit/adapter/VisitViewHolderType.kt | 25 +++ .../visit/model/VisitDetailUiModel.kt | 20 ++ .../visit/viewmodel/VisitViewModel.kt | 66 ++++++ .../visit/viewmodel/VisitViewModelFactory.kt | 10 + .../visitcreation/VisitCreationActivity.kt | 70 ++++++- .../visitcreation/VisitCreationHandler.kt | 9 + .../visitcreation/VisitCreationViewModel.kt | 65 ++++++ .../dialog/TravelSelectionFragment.kt | 68 ++++++ .../dialog/TravelSelectionHandler.kt | 7 + .../dialog/VisitedAtSelectionFragment.kt | 67 ++++++ .../dialog/VisitedAtSelectionHandler.kt | 5 + .../model/VisitCreationUiModel.kt | 35 ++++ .../visitupdate/VisitUpdateActivity.kt | 68 +++++- .../visitupdate/VisitUpdateHandler.kt | 9 + .../visitupdate/VisitUpdateViewModel.kt | 65 ++++++ .../res/drawable/shape_place_holder_oval.xml | 4 + .../drawable/shape_place_holder_rectangle.xml | 4 + .../res/layout/activity_visit_creation.xml | 193 ++++++++++++++++-- .../main/res/layout/activity_visit_update.xml | 193 ++++++++++++++++-- .../res/layout/fragment_travel_selection.xml | 43 ++++ .../src/main/res/layout/fragment_visit.xml | 36 ++-- .../layout/fragment_visited_at_selection.xml | 64 ++++++ .../src/main/res/layout/item_my_visit_log.xml | 62 ++++++ .../main/res/layout/item_others_visit_log.xml | 62 ++++++ .../main/res/layout/item_visit_default.xml | 88 ++++++++ .../src/main/res/layout/toolbar_detail.xml | 5 + .../app/src/main/res/values/strings.xml | 5 + .../app/src/main/res/values/themes.xml | 6 +- .../app/src/main/res/values/typography.xml | 8 +- 35 files changed, 1545 insertions(+), 70 deletions(-) create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/ToolbarHandler.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitAdapter.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitViewHolder.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitViewHolderType.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/model/VisitDetailUiModel.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/viewmodel/VisitViewModel.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/viewmodel/VisitViewModelFactory.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationHandler.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationViewModel.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/TravelSelectionFragment.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/TravelSelectionHandler.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/VisitedAtSelectionFragment.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/VisitedAtSelectionHandler.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/model/VisitCreationUiModel.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateHandler.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateViewModel.kt create mode 100644 android/Staccato_AN/app/src/main/res/drawable/shape_place_holder_oval.xml create mode 100644 android/Staccato_AN/app/src/main/res/drawable/shape_place_holder_rectangle.xml create mode 100644 android/Staccato_AN/app/src/main/res/layout/fragment_travel_selection.xml create mode 100644 android/Staccato_AN/app/src/main/res/layout/fragment_visited_at_selection.xml create mode 100644 android/Staccato_AN/app/src/main/res/layout/item_my_visit_log.xml create mode 100644 android/Staccato_AN/app/src/main/res/layout/item_others_visit_log.xml create mode 100644 android/Staccato_AN/app/src/main/res/layout/item_visit_default.xml diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/DeleteDialogFragment.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/DeleteDialogFragment.kt index 1101e8e7c..f9fe3054c 100644 --- a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/DeleteDialogFragment.kt +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/DeleteDialogFragment.kt @@ -9,7 +9,7 @@ import android.view.ViewGroup import androidx.fragment.app.DialogFragment import com.woowacourse.staccato.databinding.FragmentDeleteDialogBinding -class DeleteDialogFragment : DialogFragment() { +class DeleteDialogFragment(private val dialogHandler: DialogHandler) : DialogFragment() { private var _binding: FragmentDeleteDialogBinding? = null private val binding get() = _binding!! @@ -29,9 +29,20 @@ class DeleteDialogFragment : DialogFragment() { savedInstanceState: Bundle?, ): View { _binding = FragmentDeleteDialogBinding.inflate(inflater, container, false) + initButtonClickListener() return binding.root } + private fun initButtonClickListener() { + binding.btnDeleteCancel.setOnClickListener { + dismiss() + } + binding.btnDeleteConfirm.setOnClickListener { + dialogHandler.onConfirmClicked() + dismiss() + } + } + override fun onDestroyView() { super.onDestroyView() _binding = null @@ -41,3 +52,7 @@ class DeleteDialogFragment : DialogFragment() { const val TAG = "DeleteDialogFragment" } } + +fun interface DialogHandler { + fun onConfirmClicked() +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/BindingAdapters.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/BindingAdapters.kt index a295c42cd..2d7997c62 100644 --- a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/BindingAdapters.kt +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/BindingAdapters.kt @@ -1,13 +1,19 @@ package com.woowacourse.staccato.presentation import android.graphics.drawable.Drawable +import android.widget.Button import android.widget.ImageView +import android.widget.NumberPicker +import android.widget.TextView +import androidx.core.view.isGone import androidx.databinding.BindingAdapter import coil.load import coil.transform.RoundedCornersTransformation import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.request.RequestOptions +import com.woowacourse.staccato.R +import com.woowacourse.staccato.presentation.visitcreation.model.TravelUiModel @BindingAdapter( value = ["coilImageUrl", "coilPlaceHolder"], @@ -97,3 +103,68 @@ fun ImageView.setRoundedCornerImageWithGlide( .error(placeHolder) .into(this) } + +@BindingAdapter("bindSetSelectedTravel") +fun TextView.setSelectedTravel(selectedTravel: TravelUiModel?) { + if (selectedTravel == null) { + text = resources.getString(R.string.visit_creation_travel_selection_hint) + setTextColor(resources.getColor(R.color.gray3, null)) + } else { + text = selectedTravel.travelTitle + setTextColor(resources.getColor(R.color.staccato_black, null)) + } +} + +@BindingAdapter("bindSetSelectedVisitedAt") +fun TextView.setSelectedVisitedAt(selectedVisitedAt: String?) { + if (selectedVisitedAt == null) { + text = resources.getString(R.string.visit_creation_visited_at_hint) + setTextColor(resources.getColor(R.color.gray3, null)) + } else { + text = selectedVisitedAt + setTextColor(resources.getColor(R.color.staccato_black, null)) + } +} + +@BindingAdapter( + value = ["selectedTravel", "visitedAt"], +) +fun Button.setVisitUpdateButtonActive( + travel: TravelUiModel?, + visitedAt: String?, +) { + isEnabled = + if (travel == null || visitedAt == null) { + setTextColor(resources.getColor(R.color.gray4, null)) + false + } else { + setTextColor(resources.getColor(R.color.white, null)) + true + } +} + +@BindingAdapter("bindSetVisitedAtConfirmButtonActive") +fun Button.setVisitedAtConfirmButtonActive(items: List?) { + isEnabled = + if (items.isNullOrEmpty()) { + setTextColor(resources.getColor(R.color.gray4, null)) + false + } else { + setTextColor(resources.getColor(R.color.white, null)) + true + } +} + +@BindingAdapter("bindSetVisitedAtNumberPickerItems") +fun NumberPicker.setVisitedAtNumberPickerItems(items: List?) { + if (items.isNullOrEmpty()) { + isGone = true + } else { + displayedValues = items.toTypedArray() + } +} + +@BindingAdapter("bindSetVisitedAtIsEmptyVisibility") +fun TextView.setVisitedAtIsEmptyVisibility(items: List?) { + isGone = !items.isNullOrEmpty() +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/ToolbarHandler.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/ToolbarHandler.kt new file mode 100644 index 000000000..0c7e50d52 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/ToolbarHandler.kt @@ -0,0 +1,7 @@ +package com.woowacourse.staccato.presentation + +interface ToolbarHandler { + fun onUpdateClicked() + + fun onDeleteClicked() +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/VisitFragment.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/VisitFragment.kt index e2d5b5642..90732a6a7 100644 --- a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/VisitFragment.kt +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/VisitFragment.kt @@ -2,25 +2,68 @@ package com.woowacourse.staccato.presentation.visit import android.os.Bundle import android.view.View +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import com.woowacourse.staccato.DeleteDialogFragment import com.woowacourse.staccato.R import com.woowacourse.staccato.databinding.FragmentVisitBinding +import com.woowacourse.staccato.presentation.ToolbarHandler import com.woowacourse.staccato.presentation.base.BindingFragment import com.woowacourse.staccato.presentation.main.MainActivity +import com.woowacourse.staccato.presentation.visit.adapter.VisitAdapter +import com.woowacourse.staccato.presentation.visit.viewmodel.VisitViewModel +import com.woowacourse.staccato.presentation.visit.viewmodel.VisitViewModelFactory import com.woowacourse.staccato.presentation.visitupdate.VisitUpdateActivity class VisitFragment : - BindingFragment(R.layout.fragment_visit) { + BindingFragment(R.layout.fragment_visit), ToolbarHandler { + private val viewModel: VisitViewModel by viewModels { VisitViewModelFactory() } + private lateinit var visitAdapter: VisitAdapter + private val deleteDialog = DeleteDialogFragment { findNavController().popBackStack() } + override fun onViewCreated( view: View, savedInstanceState: Bundle?, ) { - binding.btnVisitUpdate.setOnClickListener { - val visitUpdateLauncher = (activity as MainActivity).visitUpdateLauncher - VisitUpdateActivity.startWithResultLauncher( - this.requireActivity(), - visitUpdateLauncher, - ) -// findNavController().navigate(R.id.action_travelFragment_to_travelUpdateFragment) + initAdapter() + initToolbarHandler() + observeData() + viewModel.fetchVisitDetailData() + } + + private fun initAdapter() { + visitAdapter = VisitAdapter(mutableListOf()) + binding.rvVisitDetail.adapter = visitAdapter + } + + private fun initToolbarHandler() { + binding.toolbarHandler = this + binding.includeVisitToolbar.toolbarDetail.setNavigationOnClickListener { + findNavController().popBackStack() + } + } + + private fun observeData() { + viewModel.visitDefault.observe(viewLifecycleOwner) { visitDefault -> + visitAdapter.updateVisitDefault(visitDefault) + } + viewModel.visitLogs.observe(viewLifecycleOwner) { visitLogs -> + visitAdapter.updateVisitLogs(visitLogs) } } + + override fun onDeleteClicked() { + val fragmentManager = parentFragmentManager + deleteDialog.apply { + show(fragmentManager, DeleteDialogFragment.TAG) + } + } + + override fun onUpdateClicked() { + val visitUpdateLauncher = (activity as MainActivity).visitUpdateLauncher + VisitUpdateActivity.startWithResultLauncher( + this.requireActivity(), + visitUpdateLauncher, + ) + } } diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitAdapter.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitAdapter.kt new file mode 100644 index 000000000..99499fb7c --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitAdapter.kt @@ -0,0 +1,76 @@ +package com.woowacourse.staccato.presentation.visit.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.woowacourse.staccato.databinding.ItemMyVisitLogBinding +import com.woowacourse.staccato.databinding.ItemVisitDefaultBinding +import com.woowacourse.staccato.presentation.visit.model.VisitDetailUiModel + +class VisitAdapter(private val items: MutableList = mutableListOf()) : + RecyclerView.Adapter() { + override fun getItemCount(): Int = items.size + + override fun getItemViewType(position: Int): Int { + return if (position == VISIT_DEFAULT_POSITION) { + VisitViewHolderType.VISIT_DEFAULT.value + } else { + VisitViewHolderType.MY_VISIT_LOG.value + } + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): VisitViewHolder { + return when (VisitViewHolderType.from(viewType)) { + VisitViewHolderType.VISIT_DEFAULT -> { + val inflater = LayoutInflater.from(parent.context) + val binding = ItemVisitDefaultBinding.inflate(inflater, parent, false) + VisitViewHolder.VisitDefaultViewHolder(binding) + } + + VisitViewHolderType.MY_VISIT_LOG -> { + val inflater = LayoutInflater.from(parent.context) + val binding = ItemMyVisitLogBinding.inflate(inflater, parent, false) + VisitViewHolder.MyVisitLogViewHolder(binding) + } + } + } + + override fun onBindViewHolder( + holder: VisitViewHolder, + position: Int, + ) { + if (holder is VisitViewHolder.VisitDefaultViewHolder) { + holder.bind(items[position] as VisitDetailUiModel.VisitDefaultUiModel) + } + if (holder is VisitViewHolder.MyVisitLogViewHolder) { + holder.bind(items[position] as VisitDetailUiModel.VisitLogUiModel) + } + } + + fun updateVisitDefault(newVisitDefault: VisitDetailUiModel.VisitDefaultUiModel) { + val result = mutableListOf(newVisitDefault) + result.addAll(items.drop(VISIT_DEFAULT_ITEM_SIZE)) + replaceAllItems(result) + notifyItemChanged(VISIT_DEFAULT_POSITION) + } + + fun updateVisitLogs(newVisitLogs: List) { + val result = items.take(VISIT_DEFAULT_ITEM_SIZE).toMutableList() + result.addAll(newVisitLogs) + replaceAllItems(result) + notifyItemRangeInserted(VISIT_DEFAULT_ITEM_SIZE, result.size) + } + + private fun replaceAllItems(result: MutableList) { + items.clear() + items.addAll(result) + } + + companion object { + private const val VISIT_DEFAULT_POSITION = 0 + private const val VISIT_DEFAULT_ITEM_SIZE = 1 + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitViewHolder.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitViewHolder.kt new file mode 100644 index 000000000..32395bce4 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitViewHolder.kt @@ -0,0 +1,23 @@ +package com.woowacourse.staccato.presentation.visit.adapter + +import androidx.databinding.ViewDataBinding +import androidx.recyclerview.widget.RecyclerView +import com.woowacourse.staccato.databinding.ItemMyVisitLogBinding +import com.woowacourse.staccato.databinding.ItemVisitDefaultBinding +import com.woowacourse.staccato.presentation.visit.model.VisitDetailUiModel + +sealed class VisitViewHolder(binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) { + class VisitDefaultViewHolder(private val binding: ItemVisitDefaultBinding) : + VisitViewHolder(binding) { + fun bind(item: VisitDetailUiModel.VisitDefaultUiModel) { + binding.visitDefault = item + } + } + + class MyVisitLogViewHolder(private val binding: ItemMyVisitLogBinding) : + VisitViewHolder(binding) { + fun bind(item: VisitDetailUiModel.VisitLogUiModel) { + binding.visitLog = item + } + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitViewHolderType.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitViewHolderType.kt new file mode 100644 index 000000000..58a947884 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/adapter/VisitViewHolderType.kt @@ -0,0 +1,25 @@ +package com.woowacourse.staccato.presentation.visit.adapter + +enum class VisitViewHolderType(val value: Int) { + VISIT_DEFAULT(0), + MY_VISIT_LOG(1), + ; + + companion object { + fun from(value: Int): VisitViewHolderType { + return when (value) { + 0 -> { + VISIT_DEFAULT + } + + 1 -> { + MY_VISIT_LOG + } + + else -> { + throw IllegalArgumentException("") + } + } + } + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/model/VisitDetailUiModel.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/model/VisitDetailUiModel.kt new file mode 100644 index 000000000..12eed935f --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/model/VisitDetailUiModel.kt @@ -0,0 +1,20 @@ +package com.woowacourse.staccato.presentation.visit.model + +sealed class VisitDetailUiModel { + data class VisitDefaultUiModel( + val visitId: Long, + val placeName: String, + val visitImage: String, + val address: String, + val visitedAt: String, + val visitedCount: Long, + ) : VisitDetailUiModel() + + data class VisitLogUiModel( + val visitLogId: Long = 0, + val memberId: Long = 0, + val nickName: String, + val memberImage: String, + val content: String, + ) : VisitDetailUiModel() +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/viewmodel/VisitViewModel.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/viewmodel/VisitViewModel.kt new file mode 100644 index 000000000..345e27d7e --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/viewmodel/VisitViewModel.kt @@ -0,0 +1,66 @@ +package com.woowacourse.staccato.presentation.visit.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.woowacourse.staccato.presentation.visit.model.VisitDetailUiModel + +class VisitViewModel : ViewModel() { + private val _visitDefault = MutableLiveData() + val visitDefault: LiveData get() = _visitDefault + + private val _visitLogs = MutableLiveData>() + val visitLogs: LiveData> get() = _visitLogs + + fun fetchVisitDetailData() { + fetchVisitDefault() + fetchVisitLogs() + } + + private fun fetchVisitDefault() { + _visitDefault.value = + VisitDetailUiModel.VisitDefaultUiModel( + 1, + "곽지해수욕장", + "https://postfiles.pstatic.net/MjAyNDA3MTZfMTMg/MDAxNzIxMDk5MjU5NTY0._coi0xlHtVjiwV7XjIABXv9EeRqi-kbJL3A7WeElA5og" + + ".mv0BFE8-Sv3TKhzZiw38YdkhGTW3NRYoO16ZHH2d5YEg.JPEG/%EC%A0%9C%EC%A3%BC%EB%8F%84.jpg?type=w966", + "제주특별자치도 제주시 애월읍 곽지리", + "2024년 7월 15일에 처음 방문했어요!", + 3, + ) + } + + private fun fetchVisitLogs() { + _visitLogs.value = + listOf( + VisitDetailUiModel.VisitLogUiModel( + 0, + 0, + "s6m1n", + "https://search.pstatic.net/common/?src=http%3A%2F%2Fimage.nmv.nav" + + "er.net%2Fcafe_2023_09_19_1290%2Fd9bcfa00-56af-11ee-9ba4-a0369ffb3258_01.jpg&type=sc960_832", + "오랜만에 우테코 친구들과 여행!\n바다 색깔이 너무 예뻤다.", + ), + VisitDetailUiModel.VisitLogUiModel( + 0, + 0, + "haena", + "https://search.pstatic.net/sunny/?src=https%3A%2F%2Fdcimg2.dcinsi" + + "de.co.kr%2Fviewimage.php%3Fid%3D2fb4de21f1ed2c%26no%3D24b0d769e1d3" + + "2ca73fe987fa1bd8233c0baf48e1ef0fb2b7008f21f62cd074543b96b120f51c61e" + + "c086ef073c6e6f22b31f9b2a3bf236a7c5adcb183d4e6504c158ab2a2ea18145a1436&type=sc960_832", + "안녕 ? 나는 해나야\n오늘은 내가 비눗방울 만드는 법을 알려줄게", + ), + VisitDetailUiModel.VisitLogUiModel( + 0, + 0, + "hodu", + "https://search.pstatic.net/common/?src=http%3A%2F%2Fcafefiles." + + "naver.net%2FMjAyMDA1MDZfMTQw%2FMDAxNTg4NzY1ODYxODA1.vFk_nJQvHMHtT" + + "IDzK7DiXYZHV-SBqiDgZzn0mxTRG0gg.sgBRmTE6nynrV5tir0KjwG_kKwytvMhVV" + + "EhZ0A6CL-8g.JPEG%2FexternalFile.jpg&type=sc960_832", + "좋은 사람, 좋은 시간.\n갓 두 ~!?", + ), + ) + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/viewmodel/VisitViewModelFactory.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/viewmodel/VisitViewModelFactory.kt new file mode 100644 index 000000000..3e561d789 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visit/viewmodel/VisitViewModelFactory.kt @@ -0,0 +1,10 @@ +package com.woowacourse.staccato.presentation.visit.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider + +class VisitViewModelFactory : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return VisitViewModel() as T + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationActivity.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationActivity.kt index cf1c66302..3e77ec7a7 100644 --- a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationActivity.kt +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationActivity.kt @@ -1,25 +1,89 @@ package com.woowacourse.staccato.presentation.visitcreation -import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle import androidx.activity.result.ActivityResultLauncher +import androidx.fragment.app.FragmentManager +import com.woowacourse.staccato.PhotoAttachFragment import com.woowacourse.staccato.R import com.woowacourse.staccato.databinding.ActivityVisitCreationBinding import com.woowacourse.staccato.presentation.base.BindingActivity +import com.woowacourse.staccato.presentation.visitcreation.dialog.TravelSelectionFragment +import com.woowacourse.staccato.presentation.visitcreation.dialog.VisitedAtSelectionFragment -class VisitCreationActivity : BindingActivity() { +class VisitCreationActivity : + BindingActivity(), + VisitCreationHandler { override val layoutResourceId = R.layout.activity_visit_creation + private val viewModel = VisitCreationViewModel() + + private val travelSelectionFragment = TravelSelectionFragment() + private val visitedAtSelectionFragment = VisitedAtSelectionFragment() + private val photoAttachFragment = PhotoAttachFragment() + private val fragmentManager: FragmentManager = supportFragmentManager override fun initStartView(savedInstanceState: Bundle?) { + initBinding() + initDialogHandler() + initVisitCreateDoneButton() + initToolbar() + observeViewModelData() + viewModel.fetchVisitCreation() + } + + private fun initBinding() { + binding.lifecycleOwner = this + binding.viewModel = viewModel + binding.visitCreationHandler = this + } + + private fun initDialogHandler() { + travelSelectionFragment.setOnTravelSelected { selectedTravel -> + viewModel.updateSelectedTravel(selectedTravel) + } + visitedAtSelectionFragment.setOnVisitedAtSelected { selectedVisitedAt -> + viewModel.updateVisitedAt(selectedVisitedAt) + } + } + + private fun initToolbar() { + binding.toolbarVisitCreation.setNavigationOnClickListener { + finish() + } + } + + private fun initVisitCreateDoneButton() { binding.btnVisitCreateDone.setOnClickListener { val resultIntent = Intent() - setResult(Activity.RESULT_OK, resultIntent) + setResult(RESULT_OK, resultIntent) finish() } } + private fun observeViewModelData() { + viewModel.visitCreationData.observe(this) { visitCreationData -> + travelSelectionFragment.setItems(visitCreationData.travels) + } + viewModel.selectedTravel.observe(this) { selectedTravel -> + val dates = selectedTravel.buildLocalDatesInRange() + viewModel.updateVisitedAt(null) + visitedAtSelectionFragment.setItems(dates) + } + } + + override fun onTravelSelectionClicked() { + travelSelectionFragment.show(fragmentManager, TravelSelectionFragment.TAG) + } + + override fun onVisitedAtClicked() { + visitedAtSelectionFragment.show(fragmentManager, VisitedAtSelectionFragment.TAG) + } + + override fun onPhotoAttachClicked() { + photoAttachFragment.show(fragmentManager, PhotoAttachFragment.TAG) + } + companion object { fun startWithResultLauncher( context: Context, diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationHandler.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationHandler.kt new file mode 100644 index 000000000..455d0e912 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationHandler.kt @@ -0,0 +1,9 @@ +package com.woowacourse.staccato.presentation.visitcreation + +interface VisitCreationHandler { + fun onTravelSelectionClicked() + + fun onVisitedAtClicked() + + fun onPhotoAttachClicked() +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationViewModel.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationViewModel.kt new file mode 100644 index 000000000..0a1185a69 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/VisitCreationViewModel.kt @@ -0,0 +1,65 @@ +package com.woowacourse.staccato.presentation.visitcreation + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.woowacourse.staccato.presentation.visitcreation.model.TravelUiModel +import com.woowacourse.staccato.presentation.visitcreation.model.VisitCreationUiModel + +class VisitCreationViewModel : ViewModel() { + private val _visitCreationData = MutableLiveData() + val visitCreationData: LiveData get() = _visitCreationData + + private val _selectedTravel = MutableLiveData() + val selectedTravel: LiveData get() = _selectedTravel + + private val _selectedVisitedAt = MutableLiveData() + val selectedVisitedAt: LiveData get() = _selectedVisitedAt + + private val dummyTravels = + listOf( + TravelUiModel( + travelId = 0, + travelTitle = "제주도", + startAt = "2024-03-01", + endAt = "2024-03-05", + ), + TravelUiModel( + travelId = 1, + travelTitle = "평택", + startAt = "2024-04-11", + endAt = "2024-04-14", + ), + TravelUiModel( + travelId = 2, + travelTitle = "강릉", + startAt = "2024-08-13", + endAt = "2024-08-16", + ), + TravelUiModel( + travelId = 3, + travelTitle = "부산", + startAt = "2024-11-28", + endAt = "2024-12-01", + ), + ) + + fun fetchVisitCreation() { + _visitCreationData.value = + VisitCreationUiModel( + pinId = 0, + placeName = "제주도 감귤 농장", + address = "제주도 남서기 서귀포시", + visitedImages = listOf(""), + travels = dummyTravels, + ) + } + + fun updateSelectedTravel(newSelectedTravel: TravelUiModel) { + _selectedTravel.value = newSelectedTravel + } + + fun updateVisitedAt(newSelectedVisitedAt: String?) { + _selectedVisitedAt.value = newSelectedVisitedAt + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/TravelSelectionFragment.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/TravelSelectionFragment.kt new file mode 100644 index 000000000..11cbfc687 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/TravelSelectionFragment.kt @@ -0,0 +1,68 @@ +package com.woowacourse.staccato.presentation.visitcreation.dialog + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.woowacourse.staccato.databinding.FragmentTravelSelectionBinding +import com.woowacourse.staccato.presentation.visitcreation.model.TravelUiModel + +class TravelSelectionFragment : BottomSheetDialogFragment() { + private var _binding: FragmentTravelSelectionBinding? = null + private val binding get() = _binding!! + private val items = mutableListOf() + + private lateinit var handler: TravelSelectionHandler + + fun setOnTravelSelected(newHandler: TravelSelectionHandler) { + handler = newHandler + } + + fun setItems(newItems: List) { + items.clear() + items.addAll(newItems) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentTravelSelectionBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + initNumberPicker() + initConfirmButton() + } + + private fun initNumberPicker() { + binding.pickerTravelSelection.apply { + minValue = 0 + maxValue = (items.size - 1).coerceAtLeast(0) + displayedValues = items.map { it.travelTitle }.toTypedArray() + wrapSelectorWheel = false + } + } + + private fun initConfirmButton() { + binding.btnTravelSelectionConfirm.setOnClickListener { + handler.onConfirmClicked(items[binding.pickerTravelSelection.value]) + dismiss() + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + companion object { + const val TAG = "TravelSelectionModalBottomSheet" + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/TravelSelectionHandler.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/TravelSelectionHandler.kt new file mode 100644 index 000000000..8516c8595 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/TravelSelectionHandler.kt @@ -0,0 +1,7 @@ +package com.woowacourse.staccato.presentation.visitcreation.dialog + +import com.woowacourse.staccato.presentation.visitcreation.model.TravelUiModel + +fun interface TravelSelectionHandler { + fun onConfirmClicked(travelUiModel: TravelUiModel) +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/VisitedAtSelectionFragment.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/VisitedAtSelectionFragment.kt new file mode 100644 index 000000000..42bb12814 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/VisitedAtSelectionFragment.kt @@ -0,0 +1,67 @@ +package com.woowacourse.staccato.presentation.visitcreation.dialog + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.woowacourse.staccato.databinding.FragmentVisitedAtSelectionBinding + +class VisitedAtSelectionFragment : BottomSheetDialogFragment() { + private var _binding: FragmentVisitedAtSelectionBinding? = null + private val binding get() = _binding!! + private val items = mutableListOf() + + private lateinit var handler: VisitedAtSelectionHandler + + fun setOnVisitedAtSelected(newHandler: VisitedAtSelectionHandler) { + handler = newHandler + } + + fun setItems(newItems: List) { + items.clear() + items.addAll(newItems) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentVisitedAtSelectionBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + binding.items = items + initNumberPicker() + initConfirmButton() + } + + private fun initNumberPicker() { + binding.pickerVisitedAt.apply { + minValue = 0 + maxValue = (items.size - 1).coerceAtLeast(0) + wrapSelectorWheel = false + } + } + + private fun initConfirmButton() { + binding.btnVisitedAtConfirm.setOnClickListener { + handler.onConfirmClicked(items[binding.pickerVisitedAt.value]) + dismiss() + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + companion object { + const val TAG = "VisitedAtSelectionModalBottomSheet" + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/VisitedAtSelectionHandler.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/VisitedAtSelectionHandler.kt new file mode 100644 index 000000000..140db1d17 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/dialog/VisitedAtSelectionHandler.kt @@ -0,0 +1,5 @@ +package com.woowacourse.staccato.presentation.visitcreation.dialog + +fun interface VisitedAtSelectionHandler { + fun onConfirmClicked(visitedAt: String) +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/model/VisitCreationUiModel.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/model/VisitCreationUiModel.kt new file mode 100644 index 000000000..d7d8b696d --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitcreation/model/VisitCreationUiModel.kt @@ -0,0 +1,35 @@ +package com.woowacourse.staccato.presentation.visitcreation.model + +import java.time.LocalDate + +data class VisitCreationUiModel( + val pinId: Long, + val placeName: String, + val address: String, + val visitedImages: List, + val travels: List, +) + +data class TravelUiModel( + val travelId: Long, + val travelTitle: String, + val startAt: String, + val endAt: String, +) { + fun buildLocalDatesInRange(): List { + val startDateNumbers = startAt.split("-").map { it.toInt() } + val endDateNumbers = endAt.split("-").map { it.toInt() } + val startDate = LocalDate.of(startDateNumbers[0], startDateNumbers[1], startDateNumbers[2]) + val endDate = LocalDate.of(endDateNumbers[0], endDateNumbers[1], endDateNumbers[2]) + return createDateList(startDate, endDate).map { it.toString() } + } + + private fun createDateList( + startDate: LocalDate, + endDate: LocalDate, + ): List { + return generateSequence(startDate) { it.plusDays(1) } + .takeWhile { !it.isAfter(endDate) } + .toList() + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateActivity.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateActivity.kt index 9a4a5ad6d..15be52136 100644 --- a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateActivity.kt +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateActivity.kt @@ -1,25 +1,87 @@ package com.woowacourse.staccato.presentation.visitupdate -import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle import androidx.activity.result.ActivityResultLauncher +import androidx.fragment.app.FragmentManager +import com.woowacourse.staccato.PhotoAttachFragment import com.woowacourse.staccato.R import com.woowacourse.staccato.databinding.ActivityVisitUpdateBinding import com.woowacourse.staccato.presentation.base.BindingActivity +import com.woowacourse.staccato.presentation.visitcreation.dialog.TravelSelectionFragment +import com.woowacourse.staccato.presentation.visitcreation.dialog.VisitedAtSelectionFragment -class VisitUpdateActivity : BindingActivity() { +class VisitUpdateActivity : BindingActivity(), VisitUpdateHandler { override val layoutResourceId = R.layout.activity_visit_update + private val viewModel = VisitUpdateViewModel() + + private val travelSelectionFragment = TravelSelectionFragment() + private val visitedAtSelectionFragment = VisitedAtSelectionFragment() + private val photoAttachFragment = PhotoAttachFragment() + private val fragmentManager: FragmentManager = supportFragmentManager override fun initStartView(savedInstanceState: Bundle?) { + initBinding() + initDialogHandler() + initVisitUpdateDoneButton() + initToolbar() + observeViewModelData() + viewModel.fetchVisitUpdate() + } + + private fun initBinding() { + binding.lifecycleOwner = this + binding.viewModel = viewModel + binding.visitUpdateHandler = this + } + + private fun initDialogHandler() { + travelSelectionFragment.setOnTravelSelected { selectedTravel -> + viewModel.updateSelectedTravel(selectedTravel) + } + visitedAtSelectionFragment.setOnVisitedAtSelected { selectedVisitedAt -> + viewModel.updateVisitedAt(selectedVisitedAt) + } + } + + private fun initToolbar() { + binding.toolbarVisitUpdate.setNavigationOnClickListener { + finish() + } + } + + private fun initVisitUpdateDoneButton() { binding.btnVisitUpdateDone.setOnClickListener { val resultIntent = Intent() - setResult(Activity.RESULT_OK, resultIntent) + setResult(RESULT_OK, resultIntent) finish() } } + private fun observeViewModelData() { + viewModel.visitUpdateData.observe(this) { visitUpdateData -> + travelSelectionFragment.setItems(visitUpdateData.travels) + } + viewModel.selectedTravel.observe(this) { selectedTravel -> + val dates = selectedTravel.buildLocalDatesInRange() + viewModel.updateVisitedAt(null) + visitedAtSelectionFragment.setItems(dates) + } + } + + override fun onTravelSelectionClicked() { + travelSelectionFragment.show(fragmentManager, TravelSelectionFragment.TAG) + } + + override fun onVisitedAtClicked() { + visitedAtSelectionFragment.show(fragmentManager, VisitedAtSelectionFragment.TAG) + } + + override fun onPhotoAttachClicked() { + photoAttachFragment.show(fragmentManager, PhotoAttachFragment.TAG) + } + companion object { fun startWithResultLauncher( context: Context, diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateHandler.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateHandler.kt new file mode 100644 index 000000000..9b251484e --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateHandler.kt @@ -0,0 +1,9 @@ +package com.woowacourse.staccato.presentation.visitupdate + +interface VisitUpdateHandler { + fun onTravelSelectionClicked() + + fun onVisitedAtClicked() + + fun onPhotoAttachClicked() +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateViewModel.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateViewModel.kt new file mode 100644 index 000000000..ac331bf5a --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/visitupdate/VisitUpdateViewModel.kt @@ -0,0 +1,65 @@ +package com.woowacourse.staccato.presentation.visitupdate + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.woowacourse.staccato.presentation.visitcreation.model.TravelUiModel +import com.woowacourse.staccato.presentation.visitcreation.model.VisitCreationUiModel + +class VisitUpdateViewModel : ViewModel() { + private val _visitUpdateData = MutableLiveData() + val visitUpdateData: LiveData get() = _visitUpdateData + + private val _selectedTravel = MutableLiveData() + val selectedTravel: LiveData get() = _selectedTravel + + private val _selectedVisitedAt = MutableLiveData() + val selectedVisitedAt: LiveData get() = _selectedVisitedAt + + private val dummyTravels = + listOf( + TravelUiModel( + travelId = 0, + travelTitle = "제주도", + startAt = "2024-03-01", + endAt = "2024-03-05", + ), + TravelUiModel( + travelId = 1, + travelTitle = "평택", + startAt = "2024-04-11", + endAt = "2024-04-14", + ), + TravelUiModel( + travelId = 2, + travelTitle = "강릉", + startAt = "2024-08-13", + endAt = "2024-08-16", + ), + TravelUiModel( + travelId = 3, + travelTitle = "부산", + startAt = "2024-11-28", + endAt = "2024-12-01", + ), + ) + + fun fetchVisitUpdate() { + _visitUpdateData.value = + VisitCreationUiModel( + pinId = 0, + placeName = "제주도 감귤 농장", + address = "제주도 남서기 서귀포시", + visitedImages = listOf(""), + travels = dummyTravels, + ) + } + + fun updateSelectedTravel(newSelectedTravel: TravelUiModel) { + _selectedTravel.value = newSelectedTravel + } + + fun updateVisitedAt(newSelectedVisitedAt: String?) { + _selectedVisitedAt.value = newSelectedVisitedAt + } +} diff --git a/android/Staccato_AN/app/src/main/res/drawable/shape_place_holder_oval.xml b/android/Staccato_AN/app/src/main/res/drawable/shape_place_holder_oval.xml new file mode 100644 index 000000000..91f82b761 --- /dev/null +++ b/android/Staccato_AN/app/src/main/res/drawable/shape_place_holder_oval.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/android/Staccato_AN/app/src/main/res/drawable/shape_place_holder_rectangle.xml b/android/Staccato_AN/app/src/main/res/drawable/shape_place_holder_rectangle.xml new file mode 100644 index 000000000..ba0b652d2 --- /dev/null +++ b/android/Staccato_AN/app/src/main/res/drawable/shape_place_holder_rectangle.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/android/Staccato_AN/app/src/main/res/layout/activity_visit_creation.xml b/android/Staccato_AN/app/src/main/res/layout/activity_visit_creation.xml index aff787e06..fb20c9d7b 100644 --- a/android/Staccato_AN/app/src/main/res/layout/activity_visit_creation.xml +++ b/android/Staccato_AN/app/src/main/res/layout/activity_visit_creation.xml @@ -5,35 +5,194 @@ + + + + + android:background="@color/white"> - -