feat(posture): Phase 2 — 기상 알람 (LYING/NON_LYING + Settings 토글 + 빨간 팝업)
요구사항: 설정 탭에서 알람 on/off → on 상태에서 일어남 감지 시 빨간 팝업.
변경:
1) ImuPostureClassifier 2-state 단순화
- 기존: SUPINE/SITTING/STANDING (3 state)
- 신규: LYING / NON_LYING (2 state) + UNKNOWN
- sitting/standing 구분 불필요 (낙상 예방엔 "누웠나/일어났나" 만 중요)
- 분류: |pitch| > threshold → LYING, ≤ → NON_LYING
2) GreenZoneConstants.pitchLyingThreshold (default 25°)
- @Volatile var — runtime 변경 가능
- dev 모드 설정에서 slider 로 조정 (10~70°, 11 steps)
3) WakeupAlarmPrefs — SharedPreferences 저장
- isEnabled (default false)
- lyingDurationSec (default 180 = 3분)
4) WakeupDetector — state machine
- LYING 진입 시 timestamp 기록
- LYING → NON_LYING 전이 시 elapsed 측정
- elapsed ≥ lyingDurationSec 이면 onWakeup 콜백
- 1회 발동 후 LYING 복귀까지 재무장 안 됨 (스팸 방지)
- UNKNOWN 은 무시 (motion artifact 흔들림 영향 X)
5) PiezoMonitoringView
- posture 아이콘 매핑 LYING (Hotel, 파랑) / NON_LYING (AccessibilityNew, 초록) / UNKNOWN
- imuCollector.onComplete 콜백에서 wakeupDetector.update(posture) 호출
- showWakeupAlarm Boolean state + 빨간 Dialog (Color 0xFFD32F2F)
· Warning 아이콘 + "환자가 일어났습니다" 메세지
· 누운 시간 표시
· 확인 버튼 (사용자 dismiss 필수, 자동 닫힘 X)
· dismissOnBackPress/ClickOutside = false (실수 dismiss 방지)
- Demo 모드: posture LYING 고정 (BLE 없음)
6) PiezoPersonalizationView — WakeupAlarmCard 신규
- 빨간 톤 카드 + Switch
- "기상 알람 — 환자가 일어나면 빨간 팝업으로 알림 (낙상 예방)"
- 토글 즉시 WakeupAlarmPrefs.setEnabled
7) Dev 설정 (PiezoMonitoringView 의 dev panel)
- "Pitch Threshold" 슬라이더 추가 (10°~70°)
- GreenZoneConstants.pitchLyingThreshold 직접 조정
- 실시간 반영 — 다음 cycle 부터 적용
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* WakeupAlarmPrefs — 기상 알람 설정 persistence (SharedPreferences).
|
||||
*
|
||||
* 사용자가 "설정 탭" 에서 toggle on/off → 일어남 감지 시 빨간 팝업.
|
||||
* LYING 유지 시간 임계 (default 3 min) 도 함께.
|
||||
*/
|
||||
package com.example.medilightv2android.services
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
||||
object WakeupAlarmPrefs {
|
||||
|
||||
private const val PREF_NAME = "wakeup_alarm_prefs"
|
||||
private const val KEY_ENABLED = "enabled"
|
||||
private const val KEY_LYING_DURATION_SEC = "lying_duration_sec"
|
||||
|
||||
private var prefs: SharedPreferences? = null
|
||||
|
||||
private fun ensure(context: Context): SharedPreferences {
|
||||
val p = prefs
|
||||
if (p != null) return p
|
||||
val newP = context.applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
prefs = newP
|
||||
return newP
|
||||
}
|
||||
|
||||
fun isEnabled(context: Context): Boolean =
|
||||
ensure(context).getBoolean(KEY_ENABLED, false)
|
||||
|
||||
fun setEnabled(context: Context, enabled: Boolean) {
|
||||
ensure(context).edit().putBoolean(KEY_ENABLED, enabled).apply()
|
||||
}
|
||||
|
||||
/** 일어남으로 인정하기 위한 직전 LYING 유지 시간 (초). default 180 = 3분. */
|
||||
fun lyingDurationSec(context: Context): Int =
|
||||
ensure(context).getInt(KEY_LYING_DURATION_SEC, 180)
|
||||
|
||||
fun setLyingDurationSec(context: Context, sec: Int) {
|
||||
ensure(context).edit().putInt(KEY_LYING_DURATION_SEC, sec).apply()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user