Skip to content

Commit 5736858

Browse files
authored
Merge pull request #54 from boostcampwm-2022/18-feature-home-bottom-sheet
[PR] Map Fragment UI 디자인 및 바텀 시트 구현
2 parents 0387bd7 + 484a80f commit 5736858

33 files changed

+515
-187
lines changed

data/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
plugins {
22
id 'com.android.library'
33
id 'org.jetbrains.kotlin.android'
4-
id 'com.google.dagger.hilt.android'
54
id 'kotlin-kapt'
5+
id 'com.google.dagger.hilt.android'
66
}
77

88
Properties properties = new Properties()
@@ -76,9 +76,9 @@ dependencies {
7676

7777
// Room
7878
implementation "androidx.room:room-runtime:2.4.3"
79+
implementation "androidx.room:room-ktx:2.4.3"
7980
annotationProcessor "androidx.room:room-compiler:2.4.3"
8081
kapt "androidx.room:room-compiler:2.4.3"
81-
implementation "androidx.room:room-ktx:2.4.3"
8282

8383
//DataStore
8484
implementation "androidx.datastore:datastore-preferences:1.0.0"

data/src/main/java/com/stop/data/local/database/dao/AlarmDao.kt

Whitespace-only changes.

presentation/src/main/java/com/stop/MainActivity.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.os.Bundle
66
import com.stop.databinding.ActivityMainBinding
77
import android.view.MotionEvent
88
import android.view.View
9+
import android.view.WindowManager
910
import android.view.inputmethod.InputMethodManager
1011
import android.widget.EditText
1112
import androidx.appcompat.app.AppCompatActivity
@@ -17,10 +18,19 @@ class MainActivity : AppCompatActivity() {
1718

1819
override fun onCreate(savedInstanceState: Bundle?) {
1920
super.onCreate(savedInstanceState)
21+
2022
binding = ActivityMainBinding.inflate(layoutInflater)
23+
hideStatusBar()
2124
setContentView(binding.root)
2225
}
2326

27+
private fun hideStatusBar() {
28+
window.setFlags(
29+
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
30+
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
31+
)
32+
}
33+
2434
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
2535
if (event.action == MotionEvent.ACTION_DOWN) {
2636
val view = currentFocus
@@ -39,7 +49,8 @@ class MainActivity : AppCompatActivity() {
3949
}
4050

4151
private fun hideKeyBoard(view: View) {
42-
val inputMethodManager = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
52+
val inputMethodManager =
53+
this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
4354
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
4455
}
4556
}

presentation/src/main/java/com/stop/ui/alarmsetting/AlarmSettingFragment.kt

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class AlarmSettingFragment : Fragment() {
2828
override fun onCreateView(
2929
inflater: LayoutInflater, container: ViewGroup?,
3030
savedInstanceState: Bundle?
31-
): View? {
31+
): View {
3232
_binding = FragmentAlarmSettingBinding.inflate(inflater, container, false)
3333

3434
initBinding()
@@ -38,29 +38,6 @@ class AlarmSettingFragment : Fragment() {
3838

3939
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
4040
super.onViewCreated(view, savedInstanceState)
41-
val test = AlarmUseCaseItem(
42-
"abc",
43-
"abc",
44-
listOf("ABC"),
45-
"abc","abc",true,true
46-
)
47-
48-
lifecycleScope.launch{
49-
alarmSettingViewModel.save(test)
50-
}
51-
52-
/* lifecycleScope.launch{
53-
alarmSettingViewModel.get().asLiveData().observe(viewLifecycleOwner){
54-
Log.e("ABC", it.toString())
55-
}
56-
}*/
57-
58-
lifecycleScope.launch{
59-
alarmSettingViewModel.get().collectLatest {
60-
Log.e("ABC", it.toString())
61-
}
62-
}
63-
6441

6542
initView()
6643
setButtonListener()
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.stop.ui.alarmsetting
2+
3+
import androidx.lifecycle.LiveData
4+
import androidx.lifecycle.MutableLiveData
5+
import androidx.lifecycle.ViewModel
6+
7+
class AlarmViewModel : ViewModel() {
8+
private val _fakeAlarmUseCaseItem = MutableLiveData<AlarmUseCaseItem>()
9+
val fakeAlarmUseCaseItem: LiveData<AlarmUseCaseItem> = _fakeAlarmUseCaseItem
10+
11+
private val _bottomSheetVisibility = MutableLiveData(false)
12+
val bottomSheetVisibility: LiveData<Boolean> = _bottomSheetVisibility
13+
14+
init {
15+
_fakeAlarmUseCaseItem.value = AlarmUseCaseItem(
16+
startPosition = "성복역 신분당선",
17+
endPosition = "이엔씨벤처드림타워3차",
18+
routes = listOf("성복역 승차", "강남역 환승", "구로디지털단지역 하차"),
19+
lastTime = "23시 50분",
20+
alarmTime = "23시 30분",
21+
alarmMethod = true,
22+
isMission = true
23+
)
24+
}
25+
26+
fun setVisibility(input: Boolean) {
27+
_bottomSheetVisibility.value = input.not()
28+
}
29+
}
30+
31+
data class AlarmUseCaseItem(
32+
val startPosition: String,
33+
val endPosition: String,
34+
val routes: List<String>,
35+
val lastTime: String,
36+
val alarmTime: String,
37+
val alarmMethod: Boolean,
38+
val isMission: Boolean
39+
)

presentation/src/main/java/com/stop/ui/map/MapFragment.kt

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.stop.ui.map
33
import android.Manifest.permission
44
import android.os.Bundle
55
import android.util.Log
6+
import android.util.TypedValue
67
import android.view.LayoutInflater
78
import android.view.View
89
import android.view.ViewGroup
@@ -12,17 +13,20 @@ import androidx.fragment.app.activityViewModels
1213
import androidx.lifecycle.flowWithLifecycle
1314
import androidx.lifecycle.lifecycleScope
1415
import androidx.navigation.findNavController
16+
import com.google.android.material.bottomsheet.BottomSheetBehavior
1517
import com.skt.tmap.TMapPoint
1618
import com.stop.R
1719
import com.stop.databinding.FragmentMapBinding
1820
import com.stop.model.Location
21+
import com.stop.ui.alarmsetting.AlarmViewModel
1922
import com.stop.ui.placesearch.PlaceSearchViewModel
2023
import kotlinx.coroutines.launch
2124

2225
class MapFragment : Fragment(), MapHandler {
2326
private var _binding: FragmentMapBinding? = null
2427
private val binding get() = _binding!!
2528

29+
private val alarmViewModel: AlarmViewModel by activityViewModels()
2630
private val placeSearchViewModel: PlaceSearchViewModel by activityViewModels()
2731

2832
private lateinit var tMap: MapTMap
@@ -47,13 +51,13 @@ class MapFragment : Fragment(), MapHandler {
4751
initView()
4852
clickSearchButton()
4953
clickEndLocation()
54+
initBottomSheetBehavior()
5055
}
5156

5257
private fun initBinding() {
53-
binding.apply {
54-
lifecycleOwner = viewLifecycleOwner
55-
viewModel = placeSearchViewModel
56-
}
58+
binding.lifecycleOwner = viewLifecycleOwner
59+
binding.placeSearchViewModel = placeSearchViewModel
60+
binding.alarmViewModel = alarmViewModel
5761
}
5862

5963
private fun initTMap() {
@@ -64,11 +68,11 @@ class MapFragment : Fragment(), MapHandler {
6468
}
6569

6670
private fun initView() {
67-
binding.imageViewCompassMode.setOnClickListener {
71+
binding.layoutCompass.setOnClickListener {
6872
tMap.tMapView.isCompassMode = tMap.tMapView.isCompassMode.not()
6973
}
7074

71-
binding.imageViewCurrentLocation.setOnClickListener {
75+
binding.layoutCurrent.setOnClickListener {
7276
requestPermissionsLauncher.launch(PERMISSIONS)
7377
tMap.tMapView.setCenterPoint(
7478
placeSearchViewModel.currentLocation.latitude,
@@ -86,8 +90,10 @@ class MapFragment : Fragment(), MapHandler {
8690
tMap.isTracking = true
8791
}
8892

89-
binding.imageViewBookmark.setOnClickListener {
90-
93+
binding.layoutBookmark.setOnClickListener {
94+
alarmViewModel.bottomSheetVisibility.value?.let {
95+
alarmViewModel.setVisibility(it)
96+
}
9197
}
9298
}
9399

@@ -98,17 +104,30 @@ class MapFragment : Fragment(), MapHandler {
98104
}
99105

100106
private fun clickEndLocation() {
101-
binding.textViewEndLocation.setOnClickListener {
107+
binding.viewPanelEnd.setOnClickListener {
102108
binding.root.findNavController().navigate(R.id.action_mapFragment_to_routeFragment)
103109
}
104110
}
105111

112+
private fun initBottomSheetBehavior() {
113+
val behavior = BottomSheetBehavior.from(binding.layoutHomeBottomSheet)
114+
115+
alarmViewModel.bottomSheetVisibility.observe(viewLifecycleOwner) {
116+
if (it) {
117+
behavior.state = BottomSheetBehavior.STATE_EXPANDED
118+
behavior.maxHeight = convertDpToPx(200)
119+
} else {
120+
behavior.maxHeight = convertDpToPx(100)
121+
}
122+
}
123+
}
124+
106125
private fun setViewVisibility() {
107126
with(binding) {
108-
textViewSearch.visibility = mapUIVisibility
109-
imageViewCompassMode.visibility = mapUIVisibility
110-
imageViewCurrentLocation.visibility = mapUIVisibility
111-
imageViewBookmark.visibility = mapUIVisibility
127+
layoutSearch.visibility = mapUIVisibility
128+
layoutCompass.visibility = mapUIVisibility
129+
layoutCurrent.visibility = mapUIVisibility
130+
layoutBookmark.visibility = mapUIVisibility
112131
}
113132
}
114133

@@ -170,8 +189,17 @@ class MapFragment : Fragment(), MapHandler {
170189
}
171190
}
172191

192+
private fun convertDpToPx(dp: Int): Int {
193+
return TypedValue.applyDimension(
194+
TypedValue.COMPLEX_UNIT_DIP,
195+
dp.toFloat(),
196+
resources.displayMetrics
197+
).toInt()
198+
}
199+
173200
override fun onDestroyView() {
174201
_binding = null
202+
175203
super.onDestroyView()
176204
}
177205

@@ -206,14 +234,14 @@ class MapFragment : Fragment(), MapHandler {
206234

207235
companion object {
208236
private const val PLACE_MARKER = "place_marker"
209-
private const val PLACE_MARKER_IMG = R.drawable.ic_baseline_location_on_32
237+
private const val PLACE_MARKER_IMG = R.drawable.ic_point_marker
210238

211239
private const val PERSON_MARKER = "marker_person_pin"
212-
private const val PERSON_MARKER_IMG = R.drawable.ic_person_pin
240+
private const val PERSON_MARKER_IMG = R.drawable.ic_person_marker
213241

214242
private const val SAME_POINT = 1
215243
val PERMISSIONS = arrayOf(permission.ACCESS_FINE_LOCATION, permission.ACCESS_COARSE_LOCATION)
216244

217-
private const val BOOKMARK_MARKER_IMG = R.drawable.ic_baseline_stars_32
245+
private const val BOOKMARK_MARKER_IMG = R.drawable.ic_bookmark_marker
218246
}
219-
}
247+
}

presentation/src/main/java/com/stop/ui/map/MapTMap.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class MapTMap(
4040

4141
companion object {
4242
private const val PLACE_MARKER = "marker"
43-
private const val PLACE_MARKER_IMG = R.drawable.ic_baseline_location_on_32
43+
private const val PLACE_MARKER_IMG = R.drawable.ic_point_marker
4444

4545
private const val SAME_POINT = 1
4646
}

presentation/src/main/java/com/stop/ui/mission/MissionTMap.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class MissionTMap(
3838

3939
companion object {
4040
private const val PERSON_MARKER = "marker_person_pin"
41-
private const val PERSON_MARKER_IMG = R.drawable.ic_person_pin
41+
private const val PERSON_MARKER_IMG = R.drawable.ic_person_marker
4242

4343
private const val PERSON_LINE = "person_line"
4444
private const val PERSON_LINE_COLOR = Color.MAGENTA

presentation/src/main/java/com/stop/ui/placesearch/PlaceSearchViewModel.kt

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.stop.ui.placesearch
22

3+
import android.location.LocationManager
34
import android.text.Editable
45
import android.util.Log
56
import android.view.View
@@ -21,6 +22,9 @@ import kotlinx.coroutines.flow.StateFlow
2122
import kotlinx.coroutines.flow.receiveAsFlow
2223
import kotlinx.coroutines.launch
2324
import javax.inject.Inject
25+
import kotlin.math.pow
26+
import kotlin.math.round
27+
import kotlin.math.sqrt
2428

2529
@HiltViewModel
2630
class PlaceSearchViewModel @Inject constructor(
@@ -46,13 +50,16 @@ class PlaceSearchViewModel @Inject constructor(
4650

4751
private val _searchKeyword = MutableStateFlow("")
4852
val searchKeyword : StateFlow<String> = _searchKeyword
49-
53+
5054
private val _geoLocation = MutableLiveData<GeoLocationInfo>()
5155
val geoLocation: LiveData<GeoLocationInfo> = _geoLocation
5256

5357
private val _panelVisibility = MutableLiveData(View.GONE)
5458
val panelVisibility: LiveData<Int> = _panelVisibility
5559

60+
private val _distance = MutableLiveData<Float>()
61+
val distance: LiveData<Float> = _distance
62+
5663
fun afterTextChanged(s: Editable?) {
5764
_searchKeyword.value = s.toString()
5865
}
@@ -74,7 +81,7 @@ class PlaceSearchViewModel @Inject constructor(
7481
} catch (e: Exception) {
7582
setNearPlaceListEmpty()
7683
errorMessageChannel.send(e.message ?: "something is wrong")
77-
Log.d("PlaceSearchViewModel","getNearPlace 실패~ ${e.toString()}")
84+
Log.d("PlaceSearchViewModel", "getNearPlace 실패~ ${e.toString()}")
7885
}
7986
}
8087
}
@@ -93,13 +100,25 @@ class PlaceSearchViewModel @Inject constructor(
93100
}
94101
}
95102

96-
fun getGeoLocationInfo(lat: Double, lon: Double) {
97-
viewModelScope.launch{
98-
_geoLocation.value = geoLocationUseCase.getGeoLocationInfo(lat, lon)
103+
fun getGeoLocationInfo(latitude: Double, longitude: Double) {
104+
viewModelScope.launch {
105+
_geoLocation.value = geoLocationUseCase.getGeoLocationInfo(latitude, longitude)
99106
_panelVisibility.value = View.VISIBLE
107+
getDistance(latitude, longitude)
100108
}
101109
}
102110

111+
private fun getDistance(latitude: Double, longitude: Double) {
112+
val startPoint = android.location.Location("Start")
113+
val endPoint = android.location.Location("End")
114+
115+
startPoint.latitude = latitude
116+
startPoint.longitude = longitude
117+
endPoint.latitude = currentLocation.latitude
118+
endPoint.longitude = currentLocation.longitude
119+
_distance.value = round(startPoint.distanceTo(endPoint) / 100) / 10
120+
}
121+
103122
companion object {
104123
private const val TMAP_VERSION = 1
105124
private val EXAMPLE_BOOKMARK_1 = Location(37.3931010, 126.9781449)

presentation/src/main/java/com/stop/ui/util/TMap.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,6 @@ open class TMap(
107107
private const val KOREA_LONGITUDE_MAX = 132.550049
108108

109109
private const val PERSON_MARKER = "marker_person_pin"
110-
private const val PERSON_MARKER_IMG = R.drawable.ic_person_pin
110+
private const val PERSON_MARKER_IMG = R.drawable.ic_person_marker
111111
}
112112
}

0 commit comments

Comments
 (0)