refactor: 알람 시스템 단순화 - 기본 알람 제거, ID 체계 개선
- 기본 알람(legacy) 로직 완전 제거 - 알람 ID 체계 단순화: alarmDbId * 100 + dayOffset - 예약 범위 축소: 오늘/내일만 예약 (복잡한 30일/365일 예약 제거) - AlarmSyncManager 단순화: 즉시 반영 로직 - AlarmReceiver 강화: 삭제된 알람 필터링 개선 - suspend 함수로 변경하여 비동기 처리 개선 Fixes: 삭제된 알람이 울리는 문제 해결 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -13,7 +13,7 @@ class AlarmReceiver : BroadcastReceiver() {
|
||||
private val TAG = "AlarmReceiver"
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
Log.d(TAG, "===== 알람 수신 (Receiver) =====")
|
||||
Log.d(TAG, "===== 알람 수신 =====")
|
||||
|
||||
// 마스터 알람이 꺼져있으면 알람 무시
|
||||
val prefs = context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
||||
@@ -22,36 +22,69 @@ class AlarmReceiver : BroadcastReceiver() {
|
||||
return
|
||||
}
|
||||
|
||||
val alarmId = intent?.getIntExtra("EXTRA_ALARM_ID", -1) ?: -1
|
||||
val isCustom = intent?.getBooleanExtra("EXTRA_IS_CUSTOM", false) ?: false
|
||||
val action = intent?.action
|
||||
|
||||
// 커스텀 알람인 경우 DB에서 여전히 유효한지 확인 (삭제된 알람이 울리는 문제 해결)
|
||||
if (isCustom && alarmId != -1) {
|
||||
val customAlarmId = intent.getIntExtra("EXTRA_UNIQUE_ID", -1)
|
||||
if (customAlarmId != -1) {
|
||||
// 비동기로 DB 확인
|
||||
val scope = CoroutineScope(Dispatchers.IO)
|
||||
scope.launch {
|
||||
val repo = ShiftRepository(context)
|
||||
val alarms = repo.getAllCustomAlarms()
|
||||
val alarmExists = alarms.any { it.id == customAlarmId && it.isEnabled }
|
||||
|
||||
if (!alarmExists) {
|
||||
Log.w(TAG, "삭제된 또는 비활성화된 알람입니다. 무시합니다. (ID: $customAlarmId)")
|
||||
scope.cancel()
|
||||
return@launch
|
||||
when (action) {
|
||||
"com.example.shiftalarm.SNOOZE" -> {
|
||||
// 스누즈 알람
|
||||
startAlarm(context, intent)
|
||||
}
|
||||
else -> {
|
||||
// 일반 알람 - DB에서 여전히 유효한지 확인
|
||||
val alarmDbId = intent?.getIntExtra("EXTRA_ALARM_DB_ID", -1) ?: -1
|
||||
|
||||
if (alarmDbId != -1) {
|
||||
// 비동기로 DB 확인
|
||||
val scope = CoroutineScope(Dispatchers.IO)
|
||||
scope.launch {
|
||||
try {
|
||||
val repo = ShiftRepository(context)
|
||||
val alarms = repo.getAllCustomAlarms()
|
||||
val alarm = alarms.find { it.id == alarmDbId }
|
||||
|
||||
// 알람이 존재하지 않거나 비활성화되었으면 무시
|
||||
if (alarm == null) {
|
||||
Log.w(TAG, "DB에서 알람을 찾을 수 없음 (삭제됨): ID=$alarmDbId")
|
||||
scope.cancel()
|
||||
return@launch
|
||||
}
|
||||
|
||||
if (!alarm.isEnabled) {
|
||||
Log.w(TAG, "알람이 비활성화됨: ID=$alarmDbId")
|
||||
scope.cancel()
|
||||
return@launch
|
||||
}
|
||||
|
||||
// 근무 조건 확인
|
||||
val dateStr = intent?.getStringExtra("EXTRA_DATE")
|
||||
if (dateStr != null) {
|
||||
val date = java.time.LocalDate.parse(dateStr)
|
||||
val team = prefs.getString("selected_team", "A") ?: "A"
|
||||
val factory = prefs.getString("selected_factory", "Jeonju") ?: "Jeonju"
|
||||
val shift = repo.getShift(date, team, factory)
|
||||
|
||||
if (alarm.shiftType != "기타" && alarm.shiftType != shift) {
|
||||
Log.w(TAG, "근무 조건이 맞지 않음: 알람=${alarm.shiftType}, 실제=$shift")
|
||||
scope.cancel()
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
// 모든 검증 통과 - 알람 실행
|
||||
startAlarm(context, intent)
|
||||
scope.cancel()
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "알람 검증 중 오류", e)
|
||||
scope.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
// 알람이 유효하면 직접 AlarmActivity 실행 + Foreground Service 시작
|
||||
} else {
|
||||
// DB ID가 없는 경우 (테스트 알람 등) - 바로 실행
|
||||
startAlarm(context, intent)
|
||||
scope.cancel()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 일반 알람은 바로 직접 실행
|
||||
startAlarm(context, intent)
|
||||
}
|
||||
|
||||
private fun startAlarm(context: Context, intent: Intent?) {
|
||||
@@ -62,15 +95,20 @@ class AlarmReceiver : BroadcastReceiver() {
|
||||
PowerManager.PARTIAL_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP,
|
||||
"ShiftAlarm::AlarmWakeLock"
|
||||
)
|
||||
wakeLock.acquire(30 * 1000L) // 30초 - Activity 실행 및 초기화에 충분한 시간
|
||||
wakeLock.acquire(30 * 1000L) // 30초
|
||||
|
||||
try {
|
||||
// 1. Foreground Service 시작 (알림 표시 및 시스템에 알람 실행 중 알림)
|
||||
val shiftType = intent?.getStringExtra("EXTRA_SHIFT_TYPE") ?: "근무"
|
||||
val soundUri = intent?.getStringExtra("EXTRA_SOUND")
|
||||
val snoozeMin = intent?.getIntExtra("EXTRA_SNOOZE", 5) ?: 5
|
||||
val snoozeRepeat = intent?.getIntExtra("EXTRA_SNOOZE_REPEAT", 3) ?: 3
|
||||
|
||||
// 1. Foreground Service 시작
|
||||
val serviceIntent = Intent(context, AlarmForegroundService::class.java).apply {
|
||||
putExtra("EXTRA_SHIFT", intent?.getStringExtra("EXTRA_SHIFT") ?: "근무")
|
||||
putExtra("EXTRA_SOUND", intent?.getStringExtra("EXTRA_SOUND"))
|
||||
putExtra("EXTRA_SNOOZE", intent?.getIntExtra("EXTRA_SNOOZE", 5) ?: 5)
|
||||
putExtra("EXTRA_SNOOZE_REPEAT", intent?.getIntExtra("EXTRA_SNOOZE_REPEAT", 3) ?: 3)
|
||||
putExtra("EXTRA_SHIFT", shiftType)
|
||||
putExtra("EXTRA_SOUND", soundUri)
|
||||
putExtra("EXTRA_SNOOZE", snoozeMin)
|
||||
putExtra("EXTRA_SNOOZE_REPEAT", snoozeRepeat)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
@@ -81,28 +119,24 @@ class AlarmReceiver : BroadcastReceiver() {
|
||||
|
||||
Log.d(TAG, "ForegroundService 시작 완료")
|
||||
|
||||
// 2. AlarmActivity 직접 실행 (알람 화면 표시)
|
||||
// 2. AlarmActivity 직접 실행
|
||||
val activityIntent = Intent(context, AlarmActivity::class.java).apply {
|
||||
putExtra("EXTRA_SHIFT", intent?.getStringExtra("EXTRA_SHIFT") ?: "근무")
|
||||
putExtra("EXTRA_SOUND", intent?.getStringExtra("EXTRA_SOUND"))
|
||||
putExtra("EXTRA_SNOOZE", intent?.getIntExtra("EXTRA_SNOOZE", 5) ?: 5)
|
||||
putExtra("EXTRA_SNOOZE_REPEAT", intent?.getIntExtra("EXTRA_SNOOZE_REPEAT", 3) ?: 3)
|
||||
// 중요: 새 태스크로 실행 (FLAG_ACTIVITY_NEW_TASK)
|
||||
putExtra("EXTRA_SHIFT", shiftType)
|
||||
putExtra("EXTRA_SOUND", soundUri)
|
||||
putExtra("EXTRA_SNOOZE", snoozeMin)
|
||||
putExtra("EXTRA_SNOOZE_REPEAT", snoozeRepeat)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
// 기존 인스턴스 재사용 및 최상위로 가져오기
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
// 잠금 화면 위에 표시
|
||||
addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
|
||||
// 화면 켜기
|
||||
addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
|
||||
}
|
||||
|
||||
// 지연 후 Activity 시작 (ForegroundService가 알림을 먼저 표시하도록)
|
||||
// 지연 후 Activity 시작 (ForegroundService가 먼저 시작되도록)
|
||||
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
||||
try {
|
||||
context.startActivity(activityIntent)
|
||||
Log.d(TAG, "AlarmActivity 실행 완료")
|
||||
Log.d(TAG, "AlarmActivity 실행 완료: $shiftType")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "AlarmActivity 실행 실패", e)
|
||||
}
|
||||
@@ -111,7 +145,7 @@ class AlarmReceiver : BroadcastReceiver() {
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "알람 실행 실패", e)
|
||||
} finally {
|
||||
// WakeLock은 Activity가 화면을 켜고 나서 해제
|
||||
// WakeLock 해제
|
||||
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
||||
if (wakeLock.isHeld) wakeLock.release()
|
||||
}, 5000)
|
||||
|
||||
Reference in New Issue
Block a user