Add polling interval settings (1-30 minutes)
- Default interval: 2 minutes - User selectable: 1, 2, 5, 10, 15, 30 minutes - Battery/data saving with longer intervals - Update README
This commit is contained in:
@@ -105,7 +105,7 @@ class MainViewModel @Inject constructor(
|
||||
workerScheduler.executeOnce()
|
||||
}
|
||||
|
||||
fun startPolling(intervalMinutes: Long = 15) {
|
||||
fun startPolling(intervalMinutes: Long = WorkerScheduler.DEFAULT_INTERVAL_MINUTES) {
|
||||
workerScheduler.schedulePeriodicPolling(intervalMinutes)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.hotdeal.alarm.presentation.components.PermissionDialog
|
||||
import com.hotdeal.alarm.presentation.main.MainUiState
|
||||
import com.hotdeal.alarm.presentation.main.MainViewModel
|
||||
import com.hotdeal.alarm.util.PermissionHelper
|
||||
import com.hotdeal.alarm.worker.WorkerScheduler
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -86,6 +87,17 @@ fun SettingsScreen(viewModel: MainViewModel) {
|
||||
)
|
||||
}
|
||||
|
||||
// 폴<> 주기 설정
|
||||
item {
|
||||
PollingIntervalSection(
|
||||
currentInterval = 2, // 기본 2분
|
||||
onIntervalChange = { minutes ->
|
||||
viewModel.stopPolling()
|
||||
viewModel.startPolling(minutes)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 사이트 선택 섹션
|
||||
item {
|
||||
Text(
|
||||
@@ -188,6 +200,80 @@ fun SettingsScreen(viewModel: MainViewModel) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PollingIntervalSection(
|
||||
currentInterval: Int,
|
||||
onIntervalChange: (Long) -> Unit
|
||||
) {
|
||||
var selectedInterval by remember { mutableStateOf(currentInterval) }
|
||||
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.3f)
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Schedule,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Text(
|
||||
text = "폴<EFBFBD> 주기 설정",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "핫딜을 확인하는 간격을 설정합니다.\n짧을수록 배터리와 데이터 소모가 증가합니다.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
// 간격 선택
|
||||
Column {
|
||||
listOf(1, 2, 5, 10, 15, 30).forEach { minutes ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(
|
||||
selected = selectedInterval == minutes,
|
||||
onClick = {
|
||||
selectedInterval = minutes
|
||||
onIntervalChange(minutes.toLong())
|
||||
}
|
||||
)
|
||||
Text(
|
||||
text = when (minutes) {
|
||||
1 -> "1분 (빠름 - 배터리 소모 큼)"
|
||||
2 -> "2분 (권장)"
|
||||
5 -> "5분 (보통)"
|
||||
10 -> "10분 (느림)"
|
||||
15 -> "15분 (매우 느림)"
|
||||
30 -> "30분 (절전)"
|
||||
else -> "${minutes}분"
|
||||
},
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PermissionStatusSection(
|
||||
permissionStatus: PermissionHelper.PermissionStatus,
|
||||
|
||||
@@ -7,23 +7,41 @@ import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Worker 스케줄러
|
||||
*
|
||||
* 기본 폴<> 주기: 2분
|
||||
* 배터리/데이터 절약을 위해 최소 2분 권장
|
||||
*/
|
||||
@Singleton
|
||||
class WorkerScheduler @Inject constructor(
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
companion object {
|
||||
private const val DEFAULT_INTERVAL_MINUTES = 15L
|
||||
const val DEFAULT_INTERVAL_MINUTES = 2L
|
||||
const val MIN_INTERVAL_MINUTES = 1L
|
||||
const val MAX_INTERVAL_MINUTES = 60L
|
||||
}
|
||||
|
||||
/**
|
||||
* 주기적 폴<> 예약
|
||||
* @param intervalMinutes 폴<> 간격 (분) - 기본 2분
|
||||
*/
|
||||
fun schedulePeriodicPolling(intervalMinutes: Long = DEFAULT_INTERVAL_MINUTES) {
|
||||
// 최소/최대 값 제한
|
||||
val safeInterval = intervalMinutes.coerceIn(MIN_INTERVAL_MINUTES, MAX_INTERVAL_MINUTES)
|
||||
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.setRequiresBatteryNotLow(true)
|
||||
.setRequiresBatteryNotLow(true) // 배터리 부족 시 실행 안 함
|
||||
.build()
|
||||
|
||||
// 유연한 실행 시간 (±25%)
|
||||
val flexTime = (safeInterval * 0.25).toLong().coerceAtLeast(1)
|
||||
|
||||
val workRequest = PeriodicWorkRequestBuilder<HotDealPollingWorker>(
|
||||
intervalMinutes, TimeUnit.MINUTES,
|
||||
intervalMinutes / 3, TimeUnit.MINUTES
|
||||
safeInterval, TimeUnit.MINUTES,
|
||||
flexTime, TimeUnit.MINUTES
|
||||
)
|
||||
.setConstraints(constraints)
|
||||
.setBackoffCriteria(
|
||||
@@ -41,6 +59,9 @@ class WorkerScheduler @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 한 번만 실행
|
||||
*/
|
||||
fun executeOnce() {
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
@@ -54,10 +75,16 @@ class WorkerScheduler @Inject constructor(
|
||||
WorkManager.getInstance(context).enqueue(workRequest)
|
||||
}
|
||||
|
||||
/**
|
||||
* 폴<> 취소
|
||||
*/
|
||||
fun cancelPolling() {
|
||||
WorkManager.getInstance(context).cancelUniqueWork(HotDealPollingWorker.WORK_NAME)
|
||||
}
|
||||
|
||||
/**
|
||||
* 폴<> 활성화 여부 확인
|
||||
*/
|
||||
fun isPollingActive(): Boolean {
|
||||
val workInfos = WorkManager.getInstance(context)
|
||||
.getWorkInfosForUniqueWork(HotDealPollingWorker.WORK_NAME)
|
||||
|
||||
Reference in New Issue
Block a user