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, "===== 알람 수신 (Receiver) =====") // 마스터 알람이 꺼져있으면 알람 무시 val prefs = context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE) if (!ShiftAlarmDefaults.isMasterAlarmEnabled(prefs)) { Log.w(TAG, "마스터 알람이 꺼져있어 알람을 무시합니다.") return } val alarmId = intent?.getIntExtra("EXTRA_ALARM_ID", -1) ?: -1 val isCustom = intent?.getBooleanExtra("EXTRA_IS_CUSTOM", false) ?: false // 커스텀 알람인 경우 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 } // 알람이 유효하면 직접 AlarmActivity 실행 + Foreground Service 시작 startAlarm(context, intent) scope.cancel() } return } } // 일반 알람은 바로 직접 실행 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초 - Activity 실행 및 초기화에 충분한 시간 try { // 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) } 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", 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) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_NO_USER_ACTION) } val pendingLaunch = android.app.PendingIntent.getActivity( context, 200, activityIntent, android.app.PendingIntent.FLAG_UPDATE_CURRENT or android.app.PendingIntent.FLAG_IMMUTABLE ) try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { val options = android.app.ActivityOptions.makeBasic().apply { setPendingIntentBackgroundActivityStartMode(android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) } pendingLaunch.send(context, 0, null, null, null, null, options.toBundle()) } else { pendingLaunch.send() } Log.d(TAG, "AlarmActivity PendingIntent 전송 완료") } catch (e: Exception) { try { context.startActivity(activityIntent) Log.d(TAG, "AlarmActivity 직접 startActivity 완료") } catch (e2: Exception) { Log.e(TAG, "AlarmActivity 실행 실패", e2) } } } catch (e: Exception) { Log.e(TAG, "알람 실행 실패", e) } finally { // WakeLock은 Activity가 화면을 켜고 나서 해제 android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ if (wakeLock.isHeld) wakeLock.release() }, 5000) } } }