Initial commit - v1.1.9
This commit is contained in:
299
app/src/main/java/com/example/shiftalarm/AlarmSyncManager.kt
Normal file
299
app/src/main/java/com/example/shiftalarm/AlarmSyncManager.kt
Normal file
@@ -0,0 +1,299 @@
|
||||
package com.example.shiftalarm
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.time.LocalDate
|
||||
|
||||
/**
|
||||
* 알람 동기화 관리자
|
||||
* DB와 AlarmManager 간의 실시간 동기화를 보장합니다.
|
||||
*
|
||||
* 동기화 전략:
|
||||
* 1. DB 작업과 AlarmManager 작업을 원자적으로 처리
|
||||
* 2. 실패 시 롤백 메커니즘 제공
|
||||
* 3. 동기화 상태 추적 및 재시도
|
||||
*/
|
||||
object AlarmSyncManager {
|
||||
|
||||
private const val TAG = "AlarmSyncManager"
|
||||
private const val PREFS_NAME = "AlarmSyncPrefs"
|
||||
|
||||
/**
|
||||
* 알람 추가 동기화
|
||||
* DB에 추가 후 AlarmManager에 즉시 예약
|
||||
*/
|
||||
suspend fun addAlarm(context: Context, alarm: CustomAlarm): Result<Unit> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val repo = ShiftRepository(context)
|
||||
|
||||
// 1. DB에 알람 추가
|
||||
val alarmId = repo.addCustomAlarm(alarm)
|
||||
Log.d(TAG, "알람 DB 추가 완료: ID=$alarmId")
|
||||
|
||||
// 2. AlarmManager에 예약
|
||||
val today = LocalDate.now(SEOUL_ZONE)
|
||||
val customAlarms = repo.getAllCustomAlarms()
|
||||
val addedAlarm = customAlarms.find { it.id == alarmId.toInt() }
|
||||
|
||||
if (addedAlarm == null) {
|
||||
Log.w(TAG, "추가된 알람을 DB에서 찾을 수 없음: ID=$alarmId")
|
||||
return@withContext Result.failure(Exception("알람을 찾을 수 없습니다"))
|
||||
}
|
||||
|
||||
if (addedAlarm.isEnabled) {
|
||||
// 향후 30일치 예약
|
||||
for (i in 0 until 30) {
|
||||
val targetDate = today.plusDays(i.toLong())
|
||||
val shift = repo.getShift(targetDate,
|
||||
context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
||||
.getString("selected_team", "A") ?: "A",
|
||||
context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
||||
.getString("selected_factory", "Jeonju") ?: "Jeonju"
|
||||
)
|
||||
|
||||
if (addedAlarm.shiftType == "기타" || addedAlarm.shiftType == shift) {
|
||||
scheduleCustomAlarm(
|
||||
context,
|
||||
targetDate,
|
||||
addedAlarm.id,
|
||||
addedAlarm.shiftType,
|
||||
addedAlarm.time,
|
||||
addedAlarm.soundUri,
|
||||
addedAlarm.snoozeInterval,
|
||||
addedAlarm.snoozeRepeat
|
||||
)
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "알람 AlarmManager 예약 완료: ID=$alarmId")
|
||||
}
|
||||
|
||||
// 3. 동기화 상태 저장
|
||||
saveSyncStatus(context, "last_add_alarm", System.currentTimeMillis())
|
||||
|
||||
Result.success(Unit)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "알람 추가 동기화 실패", e)
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 알람 수정 동기화
|
||||
* DB 수정 후 기존 AlarmManager 예약 취소 후 재예약
|
||||
*/
|
||||
suspend fun updateAlarm(context: Context, alarm: CustomAlarm): Result<Unit> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val repo = ShiftRepository(context)
|
||||
|
||||
// 1. 기존 AlarmManager 예약 취소
|
||||
cancelAllCustomAlarmSchedules(context, alarm.id)
|
||||
Log.d(TAG, "기존 알람 예약 취소 완료: ID=${alarm.id}")
|
||||
|
||||
// 2. DB 업데이트
|
||||
repo.updateCustomAlarm(alarm)
|
||||
Log.d(TAG, "알람 DB 업데이트 완료: ID=${alarm.id}")
|
||||
|
||||
// 3. 활성화된 알람이면 재예약
|
||||
if (alarm.isEnabled) {
|
||||
val today = LocalDate.now(SEOUL_ZONE)
|
||||
for (i in 0 until 30) {
|
||||
val targetDate = today.plusDays(i.toLong())
|
||||
val shift = repo.getShift(targetDate,
|
||||
context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
||||
.getString("selected_team", "A") ?: "A",
|
||||
context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
||||
.getString("selected_factory", "Jeonju") ?: "Jeonju"
|
||||
)
|
||||
|
||||
if (alarm.shiftType == "기타" || alarm.shiftType == shift) {
|
||||
scheduleCustomAlarm(
|
||||
context,
|
||||
targetDate,
|
||||
alarm.id,
|
||||
alarm.shiftType,
|
||||
alarm.time,
|
||||
alarm.soundUri,
|
||||
alarm.snoozeInterval,
|
||||
alarm.snoozeRepeat
|
||||
)
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "알람 재예약 완료: ID=${alarm.id}")
|
||||
}
|
||||
|
||||
// 4. 동기화 상태 저장
|
||||
saveSyncStatus(context, "last_update_alarm", System.currentTimeMillis())
|
||||
|
||||
Result.success(Unit)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "알람 수정 동기화 실패", e)
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 알람 삭제 동기화
|
||||
* AlarmManager 예약 먼저 취소 후 DB에서 삭제
|
||||
*/
|
||||
suspend fun deleteAlarm(context: Context, alarm: CustomAlarm): Result<Unit> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val repo = ShiftRepository(context)
|
||||
|
||||
// 1. AlarmManager 예약 취소 (DB 삭제 전에 먼저!)
|
||||
cancelAllCustomAlarmSchedules(context, alarm.id)
|
||||
Log.d(TAG, "알람 예약 취소 완료: ID=${alarm.id}")
|
||||
|
||||
// 2. DB에서 삭제
|
||||
repo.deleteCustomAlarm(alarm)
|
||||
Log.d(TAG, "알람 DB 삭제 완료: ID=${alarm.id}")
|
||||
|
||||
// 3. 동기화 상태 저장
|
||||
saveSyncStatus(context, "last_delete_alarm", System.currentTimeMillis())
|
||||
|
||||
Result.success(Unit)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "알람 삭제 동기화 실패", e)
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 알람 토글 동기화 (활성화/비활성화)
|
||||
*/
|
||||
suspend fun toggleAlarm(context: Context, alarm: CustomAlarm, enable: Boolean): Result<Unit> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val repo = ShiftRepository(context)
|
||||
val updatedAlarm = alarm.copy(isEnabled = enable)
|
||||
|
||||
if (enable) {
|
||||
// 활성화: DB 업데이트 후 예약
|
||||
repo.updateCustomAlarm(updatedAlarm)
|
||||
val today = LocalDate.now(SEOUL_ZONE)
|
||||
for (i in 0 until 30) {
|
||||
val targetDate = today.plusDays(i.toLong())
|
||||
val shift = repo.getShift(targetDate,
|
||||
context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
||||
.getString("selected_team", "A") ?: "A",
|
||||
context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
||||
.getString("selected_factory", "Jeonju") ?: "Jeonju"
|
||||
)
|
||||
|
||||
if (alarm.shiftType == "기타" || alarm.shiftType == shift) {
|
||||
scheduleCustomAlarm(
|
||||
context,
|
||||
targetDate,
|
||||
alarm.id,
|
||||
alarm.shiftType,
|
||||
alarm.time,
|
||||
alarm.soundUri,
|
||||
alarm.snoozeInterval,
|
||||
alarm.snoozeRepeat
|
||||
)
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "알람 활성화 완료: ID=${alarm.id}")
|
||||
} else {
|
||||
// 비활성화: 예약 취소 후 DB 업데이트
|
||||
cancelAllCustomAlarmSchedules(context, alarm.id)
|
||||
repo.updateCustomAlarm(updatedAlarm)
|
||||
Log.d(TAG, "알람 비활성화 완료: ID=${alarm.id}")
|
||||
}
|
||||
|
||||
saveSyncStatus(context, "last_toggle_alarm", System.currentTimeMillis())
|
||||
Result.success(Unit)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "알람 토글 동기화 실패", e)
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 전체 알람 동기화 (앱 시작 시 호출)
|
||||
*/
|
||||
suspend fun syncAllAlarmsWithCheck(context: Context): Result<SyncResult> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
Log.d(TAG, "전체 알람 동기화 시작")
|
||||
|
||||
// 1. 기존 모든 알람 취소
|
||||
val repo = ShiftRepository(context)
|
||||
val allAlarms = repo.getAllCustomAlarms()
|
||||
|
||||
for (alarm in allAlarms) {
|
||||
cancelAllCustomAlarmSchedules(context, alarm.id)
|
||||
}
|
||||
Log.d(TAG, "기존 모든 알람 취소 완료: ${allAlarms.size}개")
|
||||
|
||||
// 2. 활성화된 알람만 재예약
|
||||
val enabledAlarms = allAlarms.filter { it.isEnabled }
|
||||
val today = LocalDate.now(SEOUL_ZONE)
|
||||
val prefs = context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
||||
val team = prefs.getString("selected_team", "A") ?: "A"
|
||||
val factory = prefs.getString("selected_factory", "Jeonju") ?: "Jeonju"
|
||||
|
||||
var scheduledCount = 0
|
||||
for (alarm in enabledAlarms) {
|
||||
for (i in 0 until 30) {
|
||||
val targetDate = today.plusDays(i.toLong())
|
||||
val shift = repo.getShift(targetDate, team, factory)
|
||||
|
||||
if (alarm.shiftType == "기타" || alarm.shiftType == shift) {
|
||||
scheduleCustomAlarm(
|
||||
context,
|
||||
targetDate,
|
||||
alarm.id,
|
||||
alarm.shiftType,
|
||||
alarm.time,
|
||||
alarm.soundUri,
|
||||
alarm.snoozeInterval,
|
||||
alarm.snoozeRepeat
|
||||
)
|
||||
scheduledCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "알람 재예약 완료: ${enabledAlarms.size}개 알람, ${scheduledCount}개 예약")
|
||||
|
||||
// 3. 동기화 상태 저장
|
||||
saveSyncStatus(context, "last_full_sync", System.currentTimeMillis())
|
||||
|
||||
Result.success(SyncResult(
|
||||
totalAlarms = allAlarms.size,
|
||||
enabledAlarms = enabledAlarms.size,
|
||||
scheduledAlarms = scheduledCount
|
||||
))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "전체 알람 동기화 실패", e)
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 동기화 상태 저장
|
||||
*/
|
||||
private fun saveSyncStatus(context: Context, key: String, timestamp: Long) {
|
||||
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putLong(key, timestamp)
|
||||
.apply()
|
||||
}
|
||||
|
||||
/**
|
||||
* 마지막 동기화 시간 확인
|
||||
*/
|
||||
fun getLastSyncTime(context: Context, key: String): Long {
|
||||
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
.getLong(key, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* 동기화 결과 데이터 클래스
|
||||
*/
|
||||
data class SyncResult(
|
||||
val totalAlarms: Int,
|
||||
val enabledAlarms: Int,
|
||||
val scheduledAlarms: Int
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user