Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package com.shinhan.campung.presentation.ui.components

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import com.shinhan.campung.data.model.SharedLocation
import android.util.Log
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

/**
* 위치 공유 브로드캐스트를 수신하는 모듈화된 컴포넌트
*/
@Composable
fun LocationSharingBroadcastReceiver(
onLocationReceived: (SharedLocation) -> Unit = {},
onLocationRemoved: (String) -> Unit = {}
) {
val context = LocalContext.current

DisposableEffect(context) {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d("LocationSharingBroadcast", "브로드캐스트 수신됨 - action: ${intent?.action}")

when (intent?.action) {
"com.shinhan.campung.LOCATION_SHARED" -> {
handleLocationShared(intent, onLocationReceived)
}
"com.shinhan.campung.LOCATION_SHARING_STOPPED" -> {
handleLocationSharingStopped(intent, onLocationRemoved)
}
}
}
}

val filter = IntentFilter().apply {
addAction("com.shinhan.campung.LOCATION_SHARED")
addAction("com.shinhan.campung.LOCATION_SHARING_STOPPED")
}

// API 레벨에 따른 브로드캐스트 수신기 등록
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED)
} else {
context.registerReceiver(receiver, filter)
}
Log.d("LocationSharingBroadcast", "브로드캐스트 리시버 등록됨")

onDispose {
try {
context.unregisterReceiver(receiver)
Log.d("LocationSharingBroadcast", "브로드캐스트 리시버 해제됨")
} catch (e: IllegalArgumentException) {
Log.w("LocationSharingBroadcast", "브로드캐스트 리시버 해제 실패 (이미 해제됨)", e)
}
}
}
}

/**
* 위치 공유 브로드캐스트 처리
*/
private fun handleLocationShared(intent: Intent, onLocationReceived: (SharedLocation) -> Unit) {
Log.d("LocationSharingBroadcast", "위치 공유 브로드캐스트 처리 시작")

try {
val userName = intent.getStringExtra("userName")
val latitude = intent.getStringExtra("latitude")?.toDoubleOrNull()
val longitude = intent.getStringExtra("longitude")?.toDoubleOrNull()
val shareId = intent.getStringExtra("shareId")
val displayUntil = intent.getStringExtra("displayUntil") // 기존 코드 호환

Log.d("LocationSharingBroadcast",
"수신된 데이터 - userName: $userName, lat: $latitude, lng: $longitude, shareId: $shareId, displayUntil: $displayUntil")

if (userName != null && latitude != null && longitude != null && shareId != null && displayUntil != null) {
try {
// displayUntil 문자열을 LocalDateTime으로 파싱
val displayUntilDateTime = LocalDateTime.parse(displayUntil, DateTimeFormatter.ISO_LOCAL_DATE_TIME)

val sharedLocation = SharedLocation(
shareId = shareId,
userName = userName,
latitude = latitude,
longitude = longitude,
displayUntil = displayUntilDateTime
)

onLocationReceived(sharedLocation)
Log.d("LocationSharingBroadcast", "위치 공유 데이터 처리 완료: ${userName}님")
} catch (e: Exception) {
Log.e("LocationSharingBroadcast", "displayUntil 파싱 실패: $displayUntil", e)
// displayUntil 파싱 실패시 기본값 사용
val sharedLocation = SharedLocation(
shareId = shareId,
userName = userName,
latitude = latitude,
longitude = longitude,
displayUntil = LocalDateTime.now().plusHours(1) // 1시간 후 기본값
)
onLocationReceived(sharedLocation)
}
} else {
Log.w("LocationSharingBroadcast", "불완전한 위치 공유 데이터 수신됨")
}
} catch (e: Exception) {
Log.e("LocationSharingBroadcast", "위치 공유 브로드캐스트 처리 실패", e)
}
}

/**
* 위치 공유 중단 브로드캐스트 처리
*/
private fun handleLocationSharingStopped(intent: Intent, onLocationRemoved: (String) -> Unit) {
Log.d("LocationSharingBroadcast", "위치 공유 중단 브로드캐스트 처리 시작")

try {
val shareId = intent.getStringExtra("shareId")
val userName = intent.getStringExtra("userName")

Log.d("LocationSharingBroadcast", "위치 공유 중단 - shareId: $shareId, userName: $userName")

if (shareId != null) {
onLocationRemoved(shareId)
Log.d("LocationSharingBroadcast", "위치 공유 중단 처리 완료: $userName")
} else {
Log.w("LocationSharingBroadcast", "shareId가 없는 위치 공유 중단 브로드캐스트")
}
} catch (e: Exception) {
Log.e("LocationSharingBroadcast", "위치 공유 중단 브로드캐스트 처리 실패", e)
}
}

/**
* 통합 위치공유 브로드캐스트 수신 컴포넌트
* 기존 LocationSharingManager와 호환
*/
@Composable
fun IntegratedLocationSharingBroadcastReceiver(
locationSharingManager: com.shinhan.campung.data.service.LocationSharingManager? = null
) {
val context = LocalContext.current

DisposableEffect(context) {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d("IntegratedLocationBroadcast", "브로드캐스트 수신됨 - action: ${intent?.action}")

if (intent?.action == "com.shinhan.campung.LOCATION_SHARED") {
try {
val userName = intent.getStringExtra("userName")
val latitude = intent.getStringExtra("latitude")?.toDoubleOrNull()
val longitude = intent.getStringExtra("longitude")?.toDoubleOrNull()
val displayUntil = intent.getStringExtra("displayUntil")
val shareId = intent.getStringExtra("shareId")

Log.d("IntegratedLocationBroadcast",
"브로드캐스트 데이터: userName=$userName, lat=$latitude, lng=$longitude, displayUntil=$displayUntil, shareId=$shareId")

if (userName != null && latitude != null && longitude != null && displayUntil != null && shareId != null) {
// 기존 LocationSharingManager 사용
locationSharingManager?.addSharedLocation(
userName, latitude, longitude, displayUntil, shareId
)
Log.d("IntegratedLocationBroadcast", "LocationSharingManager.addSharedLocation 호출 완료")
} else {
Log.e("IntegratedLocationBroadcast", "브로드캐스트 데이터 누락 - 처리 중단")
}
} catch (e: Exception) {
Log.e("IntegratedLocationBroadcast", "브로드캐스트 처리 실패", e)
}
}
}
}

val filter = IntentFilter("com.shinhan.campung.LOCATION_SHARED")

// API 레벨에 따른 등록
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED)
} else {
context.registerReceiver(receiver, filter)
}

// LocalBroadcastManager도 등록
try {
androidx.localbroadcastmanager.content.LocalBroadcastManager
.getInstance(context)
.registerReceiver(receiver, filter)
Log.d("IntegratedLocationBroadcast", "LocalBroadcast 수신기도 등록 완료")
} catch (e: Exception) {
Log.e("IntegratedLocationBroadcast", "LocalBroadcast 수신기 등록 실패", e)
}

Log.d("IntegratedLocationBroadcast", "브로드캐스트 리시버 등록 완료")

onDispose {
try {
context.unregisterReceiver(receiver)
Log.d("IntegratedLocationBroadcast", "전역 브로드캐스트 수신기 해제 완료")
} catch (e: IllegalArgumentException) {
Log.w("IntegratedLocationBroadcast", "전역 브로드캐스트 수신기 해제 실패", e)
}

try {
androidx.localbroadcastmanager.content.LocalBroadcastManager
.getInstance(context)
.unregisterReceiver(receiver)
Log.d("IntegratedLocationBroadcast", "LocalBroadcast 수신기 해제 완료")
} catch (e: Exception) {
Log.w("IntegratedLocationBroadcast", "LocalBroadcast 수신기 해제 실패", e)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.navigation.NavController
import com.shinhan.campung.data.service.LocationSharingManager
import com.shinhan.campung.presentation.ui.map.SharedLocationMarkerManager
import com.shinhan.campung.presentation.ui.components.FullMapFriendLocationManager
import com.shinhan.campung.presentation.ui.components.IntegratedLocationSharingBroadcastReceiver
import com.shinhan.campung.presentation.ui.map.POIMarkerManager
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
Expand Down Expand Up @@ -160,78 +162,8 @@ fun FullMapScreen(
val currentPlayingRecord by mapViewModel.currentPlayingRecord.collectAsState()
val currentUserId by mapViewModel.currentUserId.collectAsState()

// 위치 공유 브로드캐스트 수신
DisposableEffect(context) {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
android.util.Log.d("FullMapScreen", "브로드캐스트 수신됨 - action: ${intent?.action}")

if (intent?.action == "com.shinhan.campung.LOCATION_SHARED") {
android.util.Log.d("FullMapScreen", "위치 공유 브로드캐스트 처리 시작")

val userName = intent.getStringExtra("userName")
val latitude = intent.getStringExtra("latitude")?.toDoubleOrNull()
val longitude = intent.getStringExtra("longitude")?.toDoubleOrNull()
val displayUntil = intent.getStringExtra("displayUntil")
val shareId = intent.getStringExtra("shareId")

android.util.Log.d("FullMapScreen", "브로드캐스트 데이터: userName=$userName, lat=$latitude, lng=$longitude, displayUntil=$displayUntil, shareId=$shareId")

if (userName == null || latitude == null || longitude == null || displayUntil == null || shareId == null) {
android.util.Log.e("FullMapScreen", "브로드캐스트 데이터 누락 - 처리 중단")
return
}

android.util.Log.d("FullMapScreen", "LocationSharingManager.addSharedLocation 호출")
locationSharingManager.addSharedLocation(
userName, latitude, longitude, displayUntil, shareId
)
} else {
android.util.Log.d("FullMapScreen", "다른 액션의 브로드캐스트 무시")
}
}
}

val intentFilter = IntentFilter("com.shinhan.campung.LOCATION_SHARED")
android.util.Log.d("FullMapScreen", "브로드캐스트 수신기 등록 중 - action: com.shinhan.campung.LOCATION_SHARED")

// 전역 브로드캐스트 수신기 등록
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.registerReceiver(receiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
android.util.Log.d("FullMapScreen", "전역 브로드캐스트 수신기 등록 완료 (API 33+)")
} else {
context.registerReceiver(receiver, intentFilter)
android.util.Log.d("FullMapScreen", "전역 브로드캐스트 수신기 등록 완료 (API <33)")
}

// LocalBroadcastManager도 등록 (더 안전함)
try {
androidx.localbroadcastmanager.content.LocalBroadcastManager
.getInstance(context)
.registerReceiver(receiver, intentFilter)
android.util.Log.d("FullMapScreen", "LocalBroadcast 수신기도 등록 완료")
} catch (e: Exception) {
android.util.Log.e("FullMapScreen", "LocalBroadcast 수신기 등록 실패", e)
}

onDispose {
try {
context.unregisterReceiver(receiver)
android.util.Log.d("FullMapScreen", "전역 브로드캐스트 수신기 해제 완료")
} catch (e: IllegalArgumentException) {
android.util.Log.w("FullMapScreen", "전역 브로드캐스트 수신기 해제 실패 (이미 해제됨)")
}

try {
androidx.localbroadcastmanager.content.LocalBroadcastManager
.getInstance(context)
.unregisterReceiver(receiver)
android.util.Log.d("FullMapScreen", "LocalBroadcast 수신기 해제 완료")
} catch (e: Exception) {
android.util.Log.w("FullMapScreen", "LocalBroadcast 수신기 해제 실패", e)
}
}
}
// 위치 공유 브로드캐스트 수신 (모듈화된 컴포넌트)
IntegratedLocationSharingBroadcastReceiver(locationSharingManager)

// 화면 크기
val screenHeight = configuration.screenHeightDp.dp
Expand Down Expand Up @@ -343,8 +275,7 @@ fun FullMapScreen(
}
}

// 위치 공유 마커 매니저 (모듈화됨)
val sharedLocationMarkerManager = remember { SharedLocationMarkerManager() }
// 위치 공유 마커 매니저 (모듈화된 컴포넌트로 교체됨)

// POI 마커 매니저 (모듈화됨)
var poiMarkerManager by remember { mutableStateOf<POIMarkerManager?>(null) }
Expand All @@ -358,17 +289,12 @@ fun FullMapScreen(
// cleanup()은 완전한 앱/화면 종료 시에만 호출 (콜백도 정리됨)
clusterManager?.cleanup()
poiMarkerManager?.clearPOIMarkers()
sharedLocationMarkerManager.clearAllMarkers()
// sharedLocationMarkerManager는 모듈화된 컴포넌트에서 자동 관리
Log.d("FullMapScreen", "✅ 모든 마커 매니저 완전 정리 완료")
}
}

// 위치 공유 데이터 변경 시 마커 업데이트
LaunchedEffect(sharedLocations) {
naverMapRef?.let { map ->
sharedLocationMarkerManager.updateSharedLocationMarkers(map, sharedLocations)
}
}
// 위치 공유 데이터는 모듈화된 컴포넌트에서 자동 관리

// POI 데이터 변경 시 마커 업데이트 (중복 호출 방지)
LaunchedEffect(poiData, isPOIVisible) {
Expand Down Expand Up @@ -664,7 +590,7 @@ fun FullMapScreen(
Log.d("FullMapScreen", "🔙 뒤로가기 - 모든 마커 정리 시작")
clusterManager?.clearMarkers()
poiMarkerManager?.clearPOIMarkers()
sharedLocationMarkerManager.clearAllMarkers()
// sharedLocationMarkerManager는 모듈화된 컴포넌트에서 자동 관리
Log.d("FullMapScreen", "✅ 뒤로가기 - 마커 정리 완료")
navController.popBackStack()
}
Expand Down Expand Up @@ -843,8 +769,7 @@ fun FullMapScreen(
naverMapRef?.let { map ->
mapInitializer.setupLocationOverlay(map, mapView, hasPermission, myLatLng)

// 위치 공유 마커 업데이트 (모듈화된 매니저 사용)
sharedLocationMarkerManager.updateSharedLocationMarkers(map, sharedLocations)
// 위치 공유 마커는 모듈화된 컴포넌트에서 자동 관리
}
}
},
Expand Down Expand Up @@ -938,7 +863,8 @@ fun FullMapScreen(
end = 16.dp,
bottom = 8.dp + dragHandleHeight // 바텀시트 드래그 핸들 높이만큼 위로
)
.offset(y = locationButtonOffsetY) // 바텀시트와 함께 움직임
.offset(y = locationButtonOffsetY)
.zIndex(3f)// 바텀시트와 함께 움직임
) {
Column(
horizontalAlignment = Alignment.End,
Expand Down Expand Up @@ -1112,7 +1038,8 @@ fun FullMapScreen(
onTagClick = { tagId -> mapViewModel.toggleFilterTag(tagId) },
modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = 67.dp) // 헤더 카드 아래 공간 확보
.padding(top = 67.dp)
.zIndex(3f)// 헤더 카드 아래 공간 확보
)


Expand All @@ -1129,6 +1056,16 @@ fun FullMapScreen(
bottom = 70.dp + dragHandleHeight // my_location 버튼(40dp) + 간격(14dp) + 기존패딩(16dp)
)
.offset(y = locationButtonOffsetY)
.zIndex(3f)
)

// 친구 위치공유 마커 관리 (모듈화된 컴포넌트)
FullMapFriendLocationManager(
map = naverMapRef,
onFriendClick = { friend ->
Log.d("FullMapScreen", "친구 위치 클릭: ${friend.userName}")
// 필요시 추가 처리 로직
}
)

// 애니메이션 툴팁 오버레이
Expand Down
Loading