diff --git a/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/MainActivity.kt b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/MainActivity.kt index 4d1671d..7e7a162 100644 --- a/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/MainActivity.kt +++ b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/MainActivity.kt @@ -1,18 +1,31 @@ package com.erooja.ghostrun_android import android.os.Bundle +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.erooja.ghostrun_android.permission.LocationPermissionRequester +import com.erooja.ghostrun_android.permission.LocationPermissionUtil +import com.erooja.ghostrun_android.state.CurrentLocationState +import com.erooja.ghostrun_android.state.LocationPermissionState import com.erooja.ghostrun_android.ui.theme.GhostRun_AndroidTheme +import com.erooja.ghostrun_android.util.GhostrunLocationManager import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint @@ -20,17 +33,58 @@ class MainActivity : ComponentActivity() { @Inject lateinit var permissionRequester: LocationPermissionRequester + @Inject + lateinit var locationManager: GhostrunLocationManager + + private var uiState: MainUiState by mutableStateOf(MainUiState.Loading) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { + permissionRequester + .permissionFlow + .collect { permissionState -> + Log.e("permissonState", permissionState.toString()) + when (permissionState) { + is LocationPermissionState.ObtainLocationPermission -> { + locationManager.init() + } + is LocationPermissionState.Error -> Unit + } + } + } + } + + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { + locationManager + .trackingLocationFlow + .collect { locationState -> + Log.e("locationState", locationState.toString()) + when (locationState) { + is CurrentLocationState.Success -> { + if (LocationPermissionUtil.isPermissionGranted(this@MainActivity)) { + uiState = MainUiState.Success(locationState.coordinate) + } + } + else -> Unit + } + } + } + } + setContent { permissionRequester.requestPermission() + GhostRun_AndroidTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - Greeting("Android") + CoordinateText(uiState) } } } @@ -38,17 +92,20 @@ class MainActivity : ComponentActivity() { } @Composable -fun Greeting(name: String, modifier: Modifier = Modifier) { - Text( - text = "Hello $name!", - modifier = modifier - ) +fun CoordinateText(uiState: MainUiState) { + + if (uiState is MainUiState.Success) + Column { + Text( + text = "longitude is ${uiState.coordinate.x}!", + ) + Text( + text = "latitude is ${uiState.coordinate.y}!", + ) + } } @Preview(showBackground = true) @Composable fun GreetingPreview() { - GhostRun_AndroidTheme { - Greeting("Android") - } } diff --git a/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/MainUiState.kt b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/MainUiState.kt new file mode 100644 index 0000000..5cf2609 --- /dev/null +++ b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/MainUiState.kt @@ -0,0 +1,7 @@ +package com.erooja.ghostrun_android + +import com.erooja.ghostrun_android.state.Coordinate +sealed class MainUiState { + object Loading : MainUiState() + data class Success(val coordinate: Coordinate) : MainUiState() +} diff --git a/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/permission/LocationPermissionRequester.kt b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/permission/LocationPermissionRequester.kt index 835f07e..c270eaf 100644 --- a/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/permission/LocationPermissionRequester.kt +++ b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/permission/LocationPermissionRequester.kt @@ -2,6 +2,7 @@ package com.erooja.ghostrun_android.permission import android.Manifest import android.content.Context +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.IntentSenderRequest @@ -48,12 +49,15 @@ class LocationPermissionRequester @Inject constructor( ) { permissions -> when { permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> { + Log.e("ObtainLocationPermission", "ACCESS_FINE_LOCATION") emitFlow(LocationPermissionState.ObtainLocationPermission) } permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> { + Log.e("PermissionFail", "ACCESS_COARSE_LOCATION") emitFlow(LocationPermissionState.Error.PermissionFail) } else -> { + Log.e("PermissionFail", "else") emitFlow(LocationPermissionState.Error.PermissionFail) } } diff --git a/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/permission/LocationPermissionUtil.kt b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/permission/LocationPermissionUtil.kt index d9d39d6..a4990e9 100644 --- a/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/permission/LocationPermissionUtil.kt +++ b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/permission/LocationPermissionUtil.kt @@ -8,9 +8,6 @@ import com.erooja.ghostrun_android.state.CurrentLocationState import kotlinx.coroutines.flow.MutableStateFlow object LocationPermissionUtil { - val trackingLocationFlow: MutableStateFlow = MutableStateFlow( - CurrentLocationState.UnInitialized) - val permissions = arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION diff --git a/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/state/CurrentLocationState.kt b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/state/CurrentLocationState.kt index bbd673d..ba33d11 100644 --- a/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/state/CurrentLocationState.kt +++ b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/state/CurrentLocationState.kt @@ -3,10 +3,10 @@ package com.erooja.ghostrun_android.state sealed class CurrentLocationState { object UnInitialized: CurrentLocationState() + object Prepared: CurrentLocationState() + data class Success( - val boundBottomLeftCoordinate: Coordinate = Coordinate.Default, - val boundTopRightCoordinate: Coordinate = Coordinate.Default, - val centerCoordinate: Coordinate = Coordinate.Default, + val coordinate: Coordinate = Coordinate.Default ) : CurrentLocationState() object Error : CurrentLocationState() diff --git a/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/util/GhostrunLocationManager.kt b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/util/GhostrunLocationManager.kt new file mode 100644 index 0000000..e9b7d4a --- /dev/null +++ b/GhostRun-Android/app/src/main/java/com/erooja/ghostrun_android/util/GhostrunLocationManager.kt @@ -0,0 +1,109 @@ +package com.erooja.ghostrun_android.util + +import android.annotation.SuppressLint +import android.content.Context +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import android.util.Log +import androidx.activity.ComponentActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.erooja.ghostrun_android.permission.LocationPermissionUtil +import com.erooja.ghostrun_android.state.Coordinate +import com.erooja.ghostrun_android.state.CurrentLocationState +import dagger.hilt.android.qualifiers.ActivityContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import javax.inject.Inject + +@SuppressLint("MissingPermission") +class GhostrunLocationManager @Inject constructor( + @ActivityContext private val context: Context, +) { + private val lifecycleScope: CoroutineScope + get() = (context as ComponentActivity).lifecycleScope + + private lateinit var locationManager: LocationManager + + val trackingLocationFlow: MutableStateFlow = MutableStateFlow( + CurrentLocationState.UnInitialized) + + + init { + lifecycleScope.launch { + (context as ComponentActivity).lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { + trackingLocationFlow + .filter { LocationPermissionUtil.isPermissionGranted(context) } + .filter { it == CurrentLocationState.Prepared } + .onEach { + locationManager = + context.getSystemService(ComponentActivity.LOCATION_SERVICE) as LocationManager + locationManager.requestLocationUpdates( + LocationManager.GPS_PROVIDER, + 1000, + 0f, + locationListener + ) + } + .collect() + } + } + } + + + private val locationListener = object : LocationListener { + override fun onLocationChanged(locations: MutableList) { + super.onLocationChanged(locations) + Log.e("onLocationChanged", locations.toString()) + } + + override fun onLocationChanged(location: Location) { + val longitude = location.longitude + val latitude = location.latitude + + emitFlow(CurrentLocationState.Success( + Coordinate( + longitude, + latitude + ) + )) + + Log.e("Location", "Latitude : $latitude, Longitude : $longitude") + } + + override fun onProviderEnabled(provider: String) { + super.onProviderEnabled(provider) + // provider가 사용 가능한 생태가 되는 순간 호출 + } + + override fun onProviderDisabled(provider: String) { + super.onProviderDisabled(provider) + // provider가 사용 불가능 상황이 되는 순간 호출 + } + } + + fun getLastKnownLocation(): Coordinate? { + return if (LocationPermissionUtil.isPermissionGranted(context)) { + val location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) + ?: locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) + + location?.let { Coordinate(location.longitude, location.latitude) } + } else { + null + } + } + + private fun emitFlow(state: CurrentLocationState) = lifecycleScope.launch { + trackingLocationFlow.emit(state) + } + + fun init() { + emitFlow(CurrentLocationState.Prepared) + } +}