9.1 KiB
9.1 KiB
ShiftRing 알람 시스템 기술 문서
버전 정보
- Version: 1.0.0
- Build: 100
- 지원 Android: 8.0 (Oreo) ~ 16
1. 알람 아키텍처 개요
1.1 전체 흐름
[알람 예약] → [AlarmManager] → [AlarmReceiver] → [AlarmActivity]
↑ ↓ ↓ ↓
[SharedPrefs] [Doze Mode] [WakeLock] [화면 켜짐]
1.2 핵심 컴포넌트
- AlarmUtils.kt - 알람 예약/취소 로직
- AlarmReceiver.kt - 알람 수신 및 처리
- AlarmActivity.kt - 알람 UI 및 해제
- AlarmForegroundService.kt - 포그라운드 서비스
2. 알람 예약 메커니즘
2.1 ID 생성 체계
// 기본 근무 알람: 1YYMMDD0
// 예: 2026년 2월 13일 → 126021300
fun getShiftAlarmId(date: LocalDate): Int {
return 100000000 + (date.year % 100) * 1000000 +
date.monthValue * 10000 + date.dayOfMonth * 100 + 0
}
// 사용자 알람: 2YYMMDDNN (NN = 인덱스)
// 예: 2026년 2월 13일, 인덱스 1 → 226021301
fun getCustomAlarmId(date: LocalDate, index: Int): Int {
return 200000000 + (date.year % 100) * 1000000 +
date.monthValue * 10000 + date.dayOfMonth * 100 + index
}
2.2 Android 버전별 알람 설정
Android 8.0~8.1 (API 26-27)
// Oreo: Background 제한 대응
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent)
Android 9.0~13 (API 28-33)
// Pie ~ Tiramisu: setAlarmClock 권장
val clockInfo = AlarmManager.AlarmClockInfo(triggerTime, viewPendingIntent)
alarmManager.setAlarmClock(clockInfo, pendingIntent)
Android 14+ (API 34+)
// Upside Down Cake+: 권한 필수 + setAlarmClock
// 1. canScheduleExactAlarms() 권한 체크
// 2. setAlarmClock()으로 등록
// 3. USE_FULL_SCREEN_INTENT 권한 필요
2.3 Doze 모드 대응
// Doze 모드에서도 알람 실행 보장
setExactAndAllowWhileIdle() // API 23+
setAlarmClock() // API 21+ (권장)
3. 알람 수신 및 실행
3.1 AlarmReceiver 동작
override fun onReceive(context: Context, intent: Intent?) {
// 1. WakeLock 획득 (10분)
val wakeLock = pm.newWakeLock(
FULL_WAKE_LOCK or ACQUIRE_CAUSES_WAKEUP or ON_AFTER_RELEASE,
"ShiftRing::DeepWakeLock"
)
wakeLock.acquire(10 * 60 * 1000L)
// 2. Foreground Service 시작 (Oreo+ 필수)
context.startForegroundService(serviceIntent)
// 3. Notification 발행 (전체화면 인텐트 포함)
notificationManager.notify(1001, notification)
// 4. AlarmActivity 시작
context.startActivity(fullScreenIntent)
}
3.2 화면 켜짐 로직 (버전별)
Android 8.0 (Oreo)
// Deprecated 플래그 사용
window.addFlags(
FLAG_SHOW_WHEN_LOCKED or
FLAG_DISMISS_KEYGUARD or
FLAG_TURN_SCREEN_ON
)
Android 8.1+ (Oreo MR1)
// 새로운 API 사용
setShowWhenLocked(true)
setTurnScreenOn(true)
keyguardManager.requestDismissKeyguard(this, null)
Android 14+ (Upside Down Cake)
// Keyguard dismiss 없이 위에 표시
setShowWhenLocked(true)
setTurnScreenOn(true)
window.addFlags(
FLAG_LAYOUT_IN_SCREEN or
FLAG_LAYOUT_INSET_DECOR
)
// Keyguard 해제 없이 바로 알람 화면!
4. 권한 체계
4.1 필수 권한
Android 8.0~11
WAKE_LOCK- 화면 켜짐FOREGROUND_SERVICE- 포그라운드 서비스RECEIVE_BOOT_COMPLETED- 부팅 시 알람 복구
Android 12+ (API 31+)
추가 권한:
SCHEDULE_EXACT_ALARM- 정확한 알람 예약- 설정에서 "알람 및 리마인더" 허용 필요
Android 14+ (API 34+)
추가 권한:
USE_FULL_SCREEN_INTENT- 전체화면 알림- 설정에서 "전체화면 알림" 허용 필요
- Foreground Service 타입:
specialUse
4.2 권한 설정 플로우
[앱 실행]
→ [Android 12+] "알람 및 리마인더" 권한 확인
→ 없으면 설정 화면으로 이동
→ 사용자 수동 허용
→ canScheduleExactAlarms() = true
[Android 14+]
→ "전체화면 알림" 권한 확인
→ 설정 → 권한 → 전체화면 알림 → 허용
[모든 버전]
→ 배터리 최적화 "제한 없음" 설정
→ 설정 → 배터리 → 배터리 사용량 → ShiftRing → 제한 없음
5. 근무 계산 로직
5.1 전주 공장 (4팀 순환)
val cycle = listOf(
"석간", "석간", "석간", "휴 무", "휴 무",
"주간", "주간", "주간", "주간", "주간", "휴 무", "휴 무",
"야간", "야간", "야간", "야간", "야간", "휴 무",
"석간", "석간"
) // 20일 주기
val TEAM_OFFSETS = mapOf("A" to 0, "B" to 15, "C" to 10, "D" to 5)
5.2 논산 공장 (3팀 순환)
// 주간 → 야간 → 석간 (3주 주기)
// 월~금 근무, 토~일 휴 무
5.3 알람 시간 매핑
val shiftAlarmTimes = mapOf(
"주간" to "06:00",
"석간" to "14:00",
"야간" to "22:00",
"야간 맞교대" to "18:00"
)
6. 데이터 저장 구조
6.1 SharedPreferences
ShiftAlarmPrefs:
- selected_team: String (A/B/C/D)
- selected_factory: String (Jeonju/Nonsan)
- time_ju: String (주간 알람 시간)
- time_seok: String (석간 알람 시간)
- time_ya: String (야간 알람 시간)
- custom_alarms: String (JSON 배열)
- snooze_min: Int (스누즈 시간)
6.2 Room Database
@Entity(tableName = "shift_overrides")
data class ShiftOverride(
val factory: String,
val team: String,
val date: String, // YYYY-MM-DD
val shift: String,
val manualAlarmTime: String? // HH:MM
)
7. 알람 동기화 프로세스
7.1 동기화 트리거
- 앱 실행 시 (onResume)
- 설정 변경 시 (근무/시간/팀)
- 부팅 완료 시 (BootReceiver)
- 날짜 변경 시 (매일 자정)
7.2 동기화 단계
suspend fun syncAllAlarms(context: Context) {
// 1. 기존 알람 모두 취소 (7일치)
for (date in today..today+7) {
cancelShiftAlarm(date)
cancelCustomAlarms(date)
}
// 2. 사용자 알람 JSON 파싱
val customAlarms = parseCustomAlarms()
// 3. 알람 재등록 (7일치)
for (date in today..today+7) {
val shift = calculateShift(date)
// 기본 알람 등록
if (isWorkShift(shift)) {
scheduleShiftAlarm(date, shift, alarmTime)
}
// 사용자 알람 등록
for (alarm in customAlarms) {
if (shouldTrigger(alarm, shift)) {
scheduleCustomAlarm(date, alarm)
}
}
}
}
8. 문제 해결 가이드
8.1 알람이 안 울림
원인 1: 권한 없음 (Android 12+)
증상: Log에 canScheduleExactAlarms() = false
해결:
설정 → 앱 → ShiftRing → 권한 → "알람 및 리마인더" → 허용
원인 2: 배터리 최적화
증상: Doze 모드에서 알람 지연/실패 해결:
설정 → 배터리 → 배터리 사용량 → ShiftRing → "제한 없음"
원인 3: 잠금 화면 표시 안됨 (Android 14+)
증상: 알람은 울리지만 화면 안 켜짐 해결:
설정 → 앱 → ShiftRing → 권한 → "전체화면 알림" → 허용
8.2 로그 확인 방법
# 필수 로그
adb logcat -s AlarmReceiver:D ShiftAlarm:D *:S
# 전체 로그
adb logcat | grep -E "ShiftRing|Alarm"
8.3 정상 동작 확인 로그
AlarmReceiver: ===== 알람 수신 =====
AlarmReceiver: 근무: 주간 | ID: 126021300
AlarmReceiver: WakeLock 획득 완료
AlarmReceiver: Foreground Service 시작
AlarmReceiver: Notification 발행 완료
AlarmActivity: 화면 켜짐 성공
9. 업데이트 시스템
9.1 버전 확인 URL
https://git.webpluss.net/sanjeok77/ShiftRing/version.json
9.2 version.json 형식
{
"versionCode": 100,
"versionName": "1.0.0",
"apkUrl": "https://git.webpluss.net/sanjeok77/ShiftRing/releases/download/v1.0.0/app.apk",
"changelog": "버그 수정 및 성능 개선"
}
9.3 자동 업데이트 플로우
[앱 실행]
→ [서버 버전 확인]
→ [최신 버전 비교]
→ [업데이트 다이얼로그 표시]
→ [APK 다운로드]
→ [설치 진행]
10. 릴리즈 히스토리
v1.0.0 (Build 100)
- 최초 릴리즈
- Android 8.0 ~ 16 지원
- Android 14+ 잠금 화면 위 알람 표시 개선
- Oreo 호환성 강화
부록: 개발자 참고사항
A. PendingIntent Flags
FLAG_UPDATE_CURRENT- 기존 인텐트 업데이트FLAG_IMMUTABLE- Android 12+ 필수 (보안)FLAG_NO_CREATE- 기존 확인용
B. WakeLock 종류
FULL_WAKE_LOCK- 화면 + 키보드 백라이트 켜짐ACQUIRE_CAUSES_WAKEUP- 획득 시 즉시 화면 켜짐ON_AFTER_RELEASE- 해제 후에도 일정 시간 유지
C. Foreground Service Types (Android 14+)
specialUse- 특수 목적 (알람 앱)dataSync- 데이터 동기화location- 위치 추적
문서 버전: 1.0.0
최종 수정: 2026-02-13