Files
ShiftRing/app/src/main/java/com/example/shiftalarm/AlarmReceiver.kt
sanjeok77 380b2c5a6c 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>
2026-02-28 18:10:46 +09:00

155 lines
6.7 KiB
Kotlin

package com.example.shiftalarm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.PowerManager
import android.util.Log
import kotlinx.coroutines.*
class AlarmReceiver : BroadcastReceiver() {
private val TAG = "AlarmReceiver"
override fun onReceive(context: Context, intent: Intent?) {
Log.d(TAG, "===== 알람 수신 =====")
// 마스터 알람이 꺼져있으면 알람 무시
val prefs = context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
if (!ShiftAlarmDefaults.isMasterAlarmEnabled(prefs)) {
Log.w(TAG, "마스터 알람이 꺼져있어 알람을 무시합니다.")
return
}
val action = intent?.action
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()
}
}
} else {
// DB ID가 없는 경우 (테스트 알람 등) - 바로 실행
startAlarm(context, intent)
}
}
}
}
private fun startAlarm(context: Context, intent: Intent?) {
// WakeLock 획득 (화면 켜기 및 Activity 실행 보장)
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
@Suppress("DEPRECATION")
val wakeLock = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP,
"ShiftAlarm::AlarmWakeLock"
)
wakeLock.acquire(30 * 1000L) // 30초
try {
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", shiftType)
putExtra("EXTRA_SOUND", soundUri)
putExtra("EXTRA_SNOOZE", snoozeMin)
putExtra("EXTRA_SNOOZE_REPEAT", snoozeRepeat)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent)
} else {
context.startService(serviceIntent)
}
Log.d(TAG, "ForegroundService 시작 완료")
// 2. AlarmActivity 직접 실행
val activityIntent = Intent(context, AlarmActivity::class.java).apply {
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가 먼저 시작되도록)
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
try {
context.startActivity(activityIntent)
Log.d(TAG, "AlarmActivity 실행 완료: $shiftType")
} catch (e: Exception) {
Log.e(TAG, "AlarmActivity 실행 실패", e)
}
}, 500)
} catch (e: Exception) {
Log.e(TAG, "알람 실행 실패", e)
} finally {
// WakeLock 해제
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
if (wakeLock.isHeld) wakeLock.release()
}, 5000)
}
}
}