- 기본 알람(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>
155 lines
6.7 KiB
Kotlin
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)
|
|
}
|
|
}
|
|
}
|