381 lines
9.1 KiB
Markdown
381 lines
9.1 KiB
Markdown
# ShiftRing 알람 시스템 기술 문서
|
|
|
|
## 버전 정보
|
|
- **Version**: 1.0.0
|
|
- **Build**: 100
|
|
- **지원 Android**: 8.0 (Oreo) ~ 16
|
|
|
|
---
|
|
|
|
## 1. 알람 아키텍처 개요
|
|
|
|
### 1.1 전체 흐름
|
|
```
|
|
[알람 예약] → [AlarmManager] → [AlarmReceiver] → [AlarmActivity]
|
|
↑ ↓ ↓ ↓
|
|
[SharedPrefs] [Doze Mode] [WakeLock] [화면 켜짐]
|
|
```
|
|
|
|
### 1.2 핵심 컴포넌트
|
|
1. **AlarmUtils.kt** - 알람 예약/취소 로직
|
|
2. **AlarmReceiver.kt** - 알람 수신 및 처리
|
|
3. **AlarmActivity.kt** - 알람 UI 및 해제
|
|
4. **AlarmForegroundService.kt** - 포그라운드 서비스
|
|
|
|
---
|
|
|
|
## 2. 알람 예약 메커니즘
|
|
|
|
### 2.1 ID 생성 체계
|
|
```kotlin
|
|
// 기본 근무 알람: 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)
|
|
```kotlin
|
|
// Oreo: Background 제한 대응
|
|
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent)
|
|
```
|
|
|
|
#### Android 9.0~13 (API 28-33)
|
|
```kotlin
|
|
// Pie ~ Tiramisu: setAlarmClock 권장
|
|
val clockInfo = AlarmManager.AlarmClockInfo(triggerTime, viewPendingIntent)
|
|
alarmManager.setAlarmClock(clockInfo, pendingIntent)
|
|
```
|
|
|
|
#### Android 14+ (API 34+)
|
|
```kotlin
|
|
// Upside Down Cake+: 권한 필수 + setAlarmClock
|
|
// 1. canScheduleExactAlarms() 권한 체크
|
|
// 2. setAlarmClock()으로 등록
|
|
// 3. USE_FULL_SCREEN_INTENT 권한 필요
|
|
```
|
|
|
|
### 2.3 Doze 모드 대응
|
|
```kotlin
|
|
// Doze 모드에서도 알람 실행 보장
|
|
setExactAndAllowWhileIdle() // API 23+
|
|
setAlarmClock() // API 21+ (권장)
|
|
```
|
|
|
|
---
|
|
|
|
## 3. 알람 수신 및 실행
|
|
|
|
### 3.1 AlarmReceiver 동작
|
|
```kotlin
|
|
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)
|
|
```kotlin
|
|
// Deprecated 플래그 사용
|
|
window.addFlags(
|
|
FLAG_SHOW_WHEN_LOCKED or
|
|
FLAG_DISMISS_KEYGUARD or
|
|
FLAG_TURN_SCREEN_ON
|
|
)
|
|
```
|
|
|
|
#### Android 8.1+ (Oreo MR1)
|
|
```kotlin
|
|
// 새로운 API 사용
|
|
setShowWhenLocked(true)
|
|
setTurnScreenOn(true)
|
|
keyguardManager.requestDismissKeyguard(this, null)
|
|
```
|
|
|
|
#### Android 14+ (Upside Down Cake)
|
|
```kotlin
|
|
// 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팀 순환)
|
|
```kotlin
|
|
val cycle = listOf(
|
|
"석간", "석간", "석간", "휴 무", "휴 무",
|
|
"주간", "주간", "주간", "주간", "주간", "휴 무", "휴 무",
|
|
"야간", "야간", "야간", "야간", "야간", "휴 무",
|
|
"석간", "석간"
|
|
) // 20일 주기
|
|
|
|
val TEAM_OFFSETS = mapOf("A" to 0, "B" to 15, "C" to 10, "D" to 5)
|
|
```
|
|
|
|
### 5.2 논산 공장 (3팀 순환)
|
|
```kotlin
|
|
// 주간 → 야간 → 석간 (3주 주기)
|
|
// 월~금 근무, 토~일 휴 무
|
|
```
|
|
|
|
### 5.3 알람 시간 매핑
|
|
```kotlin
|
|
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
|
|
```kotlin
|
|
@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 동기화 트리거
|
|
1. 앱 실행 시 (onResume)
|
|
2. 설정 변경 시 (근무/시간/팀)
|
|
3. 부팅 완료 시 (BootReceiver)
|
|
4. 날짜 변경 시 (매일 자정)
|
|
|
|
### 7.2 동기화 단계
|
|
```kotlin
|
|
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 로그 확인 방법
|
|
```bash
|
|
# 필수 로그
|
|
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 형식
|
|
```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
|