-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
권한 요청 및 gps, 네트워크 상태 관리 다이얼로그 추가 #49
Changes from 14 commits
4ce42b6
e0b999c
a29cc4f
ab0c958
d7ba5be
04d4a52
8b5dae4
2612454
3f5866b
9cfd619
86fdf67
4be8eda
ef889b0
51a939c
f0f76b2
eed1801
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package com.whyranoid.walkie.wlakiedialog | ||
|
||
import android.Manifest | ||
import android.app.Activity | ||
import android.os.Build | ||
import androidx.annotation.RequiresApi | ||
import androidx.compose.foundation.clickable | ||
import androidx.compose.foundation.interaction.MutableInteractionSource | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.shape.RoundedCornerShape | ||
import androidx.compose.material.AlertDialog | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.LaunchedEffect | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.draw.clip | ||
import androidx.compose.ui.platform.LocalContext | ||
import androidx.compose.ui.text.style.TextAlign | ||
import androidx.compose.ui.unit.dp | ||
import androidx.lifecycle.compose.collectAsStateWithLifecycle | ||
import com.google.accompanist.permissions.ExperimentalPermissionsApi | ||
import com.google.accompanist.permissions.isGranted | ||
import com.google.accompanist.permissions.rememberPermissionState | ||
import com.google.accompanist.permissions.shouldShowRationale | ||
import com.whyranoid.presentation.theme.WalkieColor | ||
import com.whyranoid.presentation.theme.WalkieTypography | ||
import com.whyranoid.presentation.util.openSettings | ||
import com.whyranoid.presentation.util.openStatusBar | ||
import org.koin.androidx.compose.koinViewModel | ||
|
||
/** | ||
* Provide dialog | ||
* | ||
* 권한, gps, 네트워크 상태에 따른 다이얼로그 보여주기 | ||
* | ||
* 분리한 이유: ExperimentalPermissionsApi 로써 언제든 변화 가능하기 때문에 메인 비지니스 로직, UI 로직에 포함시키지 않고 따로 분리 | ||
* | ||
*/ | ||
|
||
@RequiresApi(Build.VERSION_CODES.TIRAMISU) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 어디서 33이상의 버전을 요구하는 것인가요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 우선 저 부분이 api level 33이상 앱 상세 설정을 키는 역할을 하는데 미만 버전에서는 동작하지 않는걸로 알고있습니다.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yonghanJu 아하... 추후에 처리해보죳!! |
||
@OptIn(ExperimentalPermissionsApi::class) | ||
@Composable | ||
fun AppManageDialog() { | ||
val viewModel = koinViewModel<DialogViewModel>() | ||
val activity = LocalContext.current as Activity | ||
|
||
val locationPermissionState = rememberPermissionState( | ||
Manifest.permission.ACCESS_FINE_LOCATION, | ||
) { isGranted -> | ||
viewModel.setPermission(Manifest.permission.ACCESS_FINE_LOCATION, isGranted) | ||
} | ||
|
||
val storagePermissionState = rememberPermissionState( | ||
Manifest.permission.READ_EXTERNAL_STORAGE, | ||
) { isGranted -> | ||
viewModel.setPermission(Manifest.permission.READ_EXTERNAL_STORAGE, isGranted) | ||
} | ||
|
||
val locationDialogState = viewModel.locationPermissionDialogState.collectAsStateWithLifecycle() | ||
val storageDialogState = viewModel.storagePermissionDialogState.collectAsStateWithLifecycle() | ||
val gpsDialogState = | ||
viewModel.gpsDialogState.collectAsStateWithLifecycle(initialValue = DialogState.Initialized) | ||
val networkDialogState = | ||
viewModel.networkDialogState.collectAsStateWithLifecycle(initialValue = DialogState.Initialized) | ||
|
||
LaunchedEffect( | ||
locationDialogState.value, | ||
storageDialogState.value, | ||
gpsDialogState.value, | ||
networkDialogState.value, | ||
) { | ||
if (locationPermissionState.status.isGranted.not() && locationPermissionState.status.shouldShowRationale.not()) { | ||
locationPermissionState.launchPermissionRequest() | ||
} else if (storagePermissionState.status.isGranted.not() && storagePermissionState.status.shouldShowRationale.not()) { | ||
storagePermissionState.launchPermissionRequest() | ||
} | ||
} | ||
|
||
if (locationPermissionState.status.isGranted.not() && locationPermissionState.status.shouldShowRationale) { | ||
PermissionDialog( | ||
dialog = DialogProvider.LocationPermission, | ||
onAction = { activity.openSettings() }, | ||
modifier = Modifier.clip(RoundedCornerShape(20.dp)), | ||
) | ||
} else if (storagePermissionState.status.isGranted.not() && storagePermissionState.status.shouldShowRationale) { | ||
PermissionDialog( | ||
dialog = DialogProvider.StoragePermission, | ||
onAction = { activity.openSettings() }, | ||
modifier = Modifier.clip(RoundedCornerShape(20.dp)), | ||
) | ||
} else if (gpsDialogState.value is DialogState.InValid) { | ||
PermissionDialog( | ||
dialog = DialogProvider.GPS, | ||
onAction = { | ||
activity.openStatusBar() | ||
}, | ||
modifier = Modifier.clip(RoundedCornerShape(20.dp)), | ||
) | ||
} else if (networkDialogState.value is DialogState.InValid) { | ||
PermissionDialog( | ||
dialog = DialogProvider.Network, | ||
onAction = { | ||
activity.openStatusBar() | ||
}, | ||
modifier = Modifier.clip(RoundedCornerShape(20.dp)), | ||
) | ||
} | ||
} | ||
|
||
@Composable | ||
fun PermissionDialog( | ||
dialog: DialogProvider, | ||
onAction: () -> Unit, | ||
modifier: Modifier = Modifier, | ||
) { | ||
AlertDialog( | ||
onDismissRequest = onAction, | ||
buttons = { | ||
Column(modifier = Modifier.fillMaxWidth()) { | ||
Text( | ||
text = "동의", | ||
style = WalkieTypography.SubTitle.copy(color = WalkieColor.Primary), | ||
textAlign = TextAlign.Center, | ||
modifier = Modifier | ||
.align(Alignment.End) | ||
.clickable( | ||
indication = null, | ||
interactionSource = remember { MutableInteractionSource() }, | ||
) { | ||
onAction() | ||
} | ||
.padding(bottom = 20.dp) | ||
.padding(horizontal = 20.dp) | ||
.clip(RoundedCornerShape(12.dp)), | ||
) | ||
} | ||
}, | ||
title = { Text(dialog.title, style = WalkieTypography.SubTitle) }, | ||
text = { | ||
Text( | ||
text = dialog.description, | ||
style = WalkieTypography.Body1_Normal, | ||
) | ||
}, | ||
modifier = modifier, | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.whyranoid.walkie.wlakiedialog | ||
|
||
sealed class DialogProvider( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DialogProvider는 뭔가 Dialog전체를 제공해주는 것 같은 느낌적인 느낌... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋아요 이름 바꿔서 푸시할게요 |
||
val title: String, | ||
val description: String, | ||
) { | ||
object LocationPermission : | ||
DialogProvider( | ||
"위치 정보 제공 동의", | ||
"러닝 기능을 사용하려면 사용자의 위치 권한 동의가 반드시 필요해요.", | ||
) { | ||
const val PERMISSION = android.Manifest.permission.ACCESS_FINE_LOCATION | ||
} | ||
|
||
object StoragePermission : DialogProvider( | ||
"미디어 및 파일 접근 동의", | ||
"러닝 정보를 기록하려면 미디어 및 파일 접근 권한 동의가 반드시 필요해요.", | ||
) { | ||
const val PERMISSION = android.Manifest.permission.READ_EXTERNAL_STORAGE | ||
} | ||
|
||
object GPS : DialogProvider( | ||
"위치 상태 확인 요망", | ||
"러닝 기능을 사용하려면 위치 정보 상태가 켜져야해요.", | ||
) | ||
|
||
object Network : DialogProvider( | ||
"네트워크 상태 확인 요망", | ||
"러닝 기능을 사용하려면 네트워트가 연결되어야해요.", | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.whyranoid.walkie.wlakiedialog | ||
|
||
sealed class DialogState { | ||
object Initialized : DialogState() | ||
|
||
object Valid : DialogState() | ||
|
||
data class InValid(val dialogProvider: DialogProvider) : DialogState() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.whyranoid.walkie.wlakiedialog | ||
|
||
import androidx.lifecycle.ViewModel | ||
import com.whyranoid.domain.usecase.broadcast.AddGpsListener | ||
import com.whyranoid.domain.usecase.broadcast.AddNetworkListener | ||
import com.whyranoid.domain.usecase.broadcast.GetGpsState | ||
import com.whyranoid.domain.usecase.broadcast.GetNetworkState | ||
import com.whyranoid.domain.usecase.broadcast.RemoveGpsListener | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.asStateFlow | ||
import kotlinx.coroutines.flow.map | ||
|
||
class DialogViewModel( | ||
private val addNetworkListener: AddNetworkListener, | ||
private val removeNetworkListener: AddNetworkListener, | ||
private val getNetworkState: GetNetworkState, | ||
private val addGpsListener: AddGpsListener, | ||
private val removeGpsListener: RemoveGpsListener, | ||
private val getGpsState: GetGpsState, | ||
) : ViewModel() { | ||
init { | ||
addNetworkListener() | ||
addGpsListener() | ||
} | ||
|
||
private val _locationPermissionDialogState = | ||
MutableStateFlow<DialogState>(DialogState.Initialized) | ||
private val _storagePermissionDialogState = | ||
MutableStateFlow<DialogState>(DialogState.Initialized) | ||
|
||
val locationPermissionDialogState get() = _locationPermissionDialogState.asStateFlow() | ||
val storagePermissionDialogState get() = _storagePermissionDialogState.asStateFlow() | ||
|
||
val gpsDialogState = getGpsState().map { | ||
if (it) DialogState.Valid else DialogState.InValid(DialogProvider.GPS) | ||
} | ||
val networkDialogState = getNetworkState().map { | ||
if (it) DialogState.Valid else DialogState.InValid(DialogProvider.Network) | ||
} | ||
|
||
fun setPermission(permission: String, showDialog: Boolean) { | ||
if (permission == DialogProvider.LocationPermission.PERMISSION) { | ||
_locationPermissionDialogState.value = | ||
if (showDialog) DialogState.Valid else DialogState.InValid(DialogProvider.LocationPermission) | ||
} else if (permission == DialogProvider.StoragePermission.PERMISSION) { | ||
_storagePermissionDialogState.value = | ||
if (showDialog) DialogState.Valid else DialogState.InValid(DialogProvider.StoragePermission) | ||
} | ||
} | ||
|
||
override fun onCleared() { | ||
removeGpsListener() | ||
removeNetworkListener() | ||
super.onCleared() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.whyranoid.domain.usecase.broadcast | ||
|
||
import com.whyranoid.domain.repository.GpsRepository | ||
|
||
class AddGpsListener(private val gpsRepository: GpsRepository) { | ||
operator fun invoke() { | ||
gpsRepository.registerReceiver() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.whyranoid.domain.usecase.broadcast | ||
|
||
import com.whyranoid.domain.repository.NetworkRepository | ||
|
||
class AddNetworkListener(private val networkRepository: NetworkRepository) { | ||
operator fun invoke() { | ||
networkRepository.addNetworkConnectionCallback() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.whyranoid.domain.usecase.broadcast | ||
|
||
import com.whyranoid.domain.repository.GpsRepository | ||
import kotlinx.coroutines.flow.StateFlow | ||
|
||
class GetGpsState(private val gpsRepository: GpsRepository) { | ||
operator fun invoke(): StateFlow<Boolean> { | ||
return gpsRepository.getGpsEnabledState() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
얜 뭔가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gps, 네트워크가 없을 때 뜨는 다이얼로그에 연결동작으로 상단 상태바 내려와서 바로 설정할 수 있게 해주는데 필요한 권한입니다