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) } } }