Commit Graph

238 Commits

Author SHA1 Message Date
dw.jang 9d32d13452 fix: BLE reconnect를 fresh scan→connect 방식으로 변경
기존: connectGatt(autoConnect=true) — Android에서 매우 불안정, 수분 걸림
변경: BLE 스캔으로 기기를 다시 찾은 후 connectGatt(autoConnect=false)로 빠르게 연결

재연결 흐름:
1. watchdog 15초 타임아웃 → forceDisconnectAndReconnect()
2. scheduleAutoReconnect() → 3s/5s/10s 백오프
3. attemptAutoReconnect():
   - BLE 스캔 시작 (기기 이름 or MAC 주소 매칭)
   - 발견 시 즉시 connectGatt(autoConnect=false) → 빠른 연결
   - 8초 스캔 타임아웃 → 다음 attempt
   - 최대 5회 시도 후 실패 처리
4. 모든 단계 BleDebugLogger에 기록

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 14:02:08 +09:00
dw.jang d4e2627efe fix: BLE watchdog — 좀비 연결 감지 + 강제 reconnect
Android BLE가 disconnect 콜백을 안 보내는 경우(좀비 연결) 대응:
- 마지막 RX로부터 15초간 응답 없으면 강제 disconnect → auto reconnect
- processReceivedData()에서 lastRxTimestamp 갱신
- 5초마다 watchdog 체크
- WATCHDOG_TIMEOUT 이벤트 BleDebugLogger에 기록
- 수동 disconnect 시 watchdog 해제

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 12:27:13 +09:00
dw.jang 506248655d fix: BLE 로그 Downloads 폴더에 저장 (파일 탐색기 접근 가능)
- exportToDownloads(): Downloads/VesiScan_BLE_날짜.log 로 저장
- 실패 시 fallback으로 내부 저장소에 저장
- Toast로 저장 경로 안내

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 12:22:07 +09:00
dw.jang 6f4488f1ae fix: BV 측정 최소 center 3채널 이상 요구 + 실패 시 사용자 안내
- center 채널 3개 미만이면 BV 계산 스킵 (신뢰도 부족 방지)
- 1~2채널: "측정 부족 (N/4채널) — 센서 위치를 조절해주세요"
- 0채널: "방광이 감지되지 않았습니다. 센서 접촉을 확인해주세요."
- BleDebugLogger에 BV_SKIP/BV_FAIL 기록

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 12:08:52 +09:00
dw.jang f220cca187 feat: BLE 디버그 로거 — 펌웨어 디버깅용 상세 통신 로그
BleDebugLogger 신규:
- TX/RX 패킷 기록 (타임스탬프 ms, 데이터 크기)
- RSSI 실시간 추적 (5초마다 readRemoteRssi)
- 연결/끊김/재연결 이벤트 + GATT status code
- 패킷 타임아웃, 채널 누락, Endian 감지 로그
- 측정 결과 (volumeMl, lrRatio, validChannels)
- 파일 내보내기 (.log / .jsonl) + 클립보드 복사
- 세션 통계 (TX/RX/ERR/RSSI avg/min/max)

BleManager 연동:
- sendRaw() → TX 로그
- processReceivedData() → RX 로그
- connected/disconnected/reconnecting/reconnectFailed 이벤트
- onReadRemoteRssi 콜백 → RSSI 기록

PiezoMonitoringView:
- Developer Tools 하단에 BLE Debug Panel 추가
- 2초 자동 갱신, 색상 코딩 (TX=파랑, RX=초록, ERR=빨강, RSSI=노랑, CONN=보라)
- 클립보드 복사 / 파일 저장 / 클리어 버튼

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 11:47:36 +09:00
dw.jang 079774c869 feat: Placement Guide 실시간 연속 스캔 (3초 간격)
- 수동 1회 → 자동 반복 스캔 (Start/Stop 토글)
- 3초마다 maa 전송 → Score 실시간 갱신
- 사용자가 센서 움직이면서 점수 변화를 즉시 확인 가능
- "Scanning every 3s — move sensor and watch the score" 안내 문구

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 11:29:02 +09:00
dw.jang c114592ab1 feat: 6ch 알고리즘 포팅 + urineLen 기반 정밀 Placement Guide
6ch 알고리즘 (config_6ch.py / bv_estimation.py 포팅):
- PiezoHW: V0/V1/V2 프리셋 (Snell's law 굴절 각도)
- computeLrRatio(): CH4/CH5 lateral에서 타원 단면 비율 동적 계산
- estimateBladderVolume6ch(): center 4ch + lateral lr_ratio
- denoise(): TVD 제거, SG 필터만 사용
- LOW_ECHO_AMP: 1600→1150, distancePerSample: 1.771

Placement Guide 2단계:
- 1단계: 이진 판정 (잡힘/안잡힘) → 기본 방향
- 2단계: urineLen 기반 정밀 가이드 (대칭+균형+크기)
  - 세로: CH0/CH3 비율 1.5배 이상이면 방향 제시
  - 가로: CH4/CH5 차이 5칸 이상이면 방향 제시
- Placement Score = 세로대칭(30%) + 가로균형(30%) + 중앙크기(40%)
- 각 채널에 urineLen 숫자 표시

VesiScan_Android_Pipeline_Summary.md 문서 작성

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 10:22:57 +09:00
dw.jang a95815c42d fix: Piezo 진입 시 bladderLevel 0으로 초기화 (이전 세션 값 잔류 방지)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 17:46:16 +09:00
dw.jang 6f5801b4b6 feat: BLE 자동 reconnect + 배터리 폴링 5초 (keep-alive)
- BleManager: 배터리 폴링 30초→5초 (펌웨어 15초 타임아웃 방지, iOS 동일)
- BleManager: 자동 reconnect with 백오프 (3s→5s→10s, 최대 5회)
  - scheduleAutoReconnect() + attemptAutoReconnect()
  - GATT 에러 시에도 백오프 재시도
  - 수동 disconnect 시 자동 reconnect 취소
- PiezoMonitoringView: 끊김 시 즉시 파란 배너(RECONNECTING)
  - 성공 시 배너 자동 숨김 + 배터리 폴링 재시작
  - 5회 실패 시 빨간 배너 + 수동 Reconnect 버튼
  - 측정 중 끊김 → isMeasuring 리셋 + 자동측정 중지

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 17:40:00 +09:00
dw.jang d8a725b21b fix: 도넛차트 recomposition 버그 + Spot 롱프레스 자동측정 + BV 4채널(CH0~CH3)
- bladderLevel.copy() 할당으로 Compose recomposition 트리거 (도넛차트 실시간 갱신)
- Spot 버튼: Button→Box+detectTapGestures (탭=단일, 롱프레스=30초 자동측정)
- startAutoMeasure() + LaunchedEffect 30초 반복 루프
- BV 추정 CH0~CH4(5ch)→CH0~CH3(4ch) 세로 채널만 사용

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 17:23:46 +09:00
dw.jang 5cc2321ff4 fix: placement direction guide 수정 + sensor layout 확대 + BLE prefix 필터(2025/VBT)
- PlacementGuideView: 방향 가이드 로직 수정 (CH0=배꼽, CH3=치골 기준)
  CH0✅CH3❌→UP, CH3✅CH0❌→DOWN, 좌우도 동일하게 보정
- SensorLayoutCanvas: 스케일 16px/mm, 방광 280×220, body 400×400
- DeviceScanView: BLE prefix 필터 "2025" | "VBT"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 16:19:53 +09:00
dw.jang d8db7671ac feat: iOS 동기화 — 알림설정, Quick Save, 일지 요약바, 디자인토큰, BLE필터 제거, 그래프 축·클립·Placement 확대
- ReminderSettingsView: 간격 리스트+체크, 유동 토글, 팝업 알림, 요약, 권한 배너 (iOS ReminderView 동일)
- ReminderService: 기본값 iOS 동기화(120분, 8시), 5종 랜덤 메시지, usePopup/isFlexible/permissionGranted
- PiezoMonitoringView: 배뇨기록 다이얼로그 iOS DiaryRecordSheet 스타일 + Quick Save
- VoidingDiaryView: 요약 바(건수+총량+평균), 의료지표 4+3 배열, 총 횟수 추가
- DesignTokens: iOS DS enum과 동일 값 (44dp touch, 12dp medium radius, spacing md=16/lg=24/xl=32)
- DeviceScanView: BLE prefix 필터 제거 (VBT 등 신규 기기 지원)
- WaveformChart: Y축(amplitude)/X축(depth mm) 라벨, clip으로 셀 오버플로 방지
- PlacementGuideView: 센서 레이아웃 확대(300→400dp), 스캔 후 채널별 에코 그래프 표시

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 14:03:02 +09:00
dwjang 5a084ac5a9 feat: auto-detect device preset from BLE name
VBT260400xy → FLAT, VBT260402xy → NEW_CASE2, VBT260403xy → NEW_CASE1
2025MEDIP → LEGACY_5CH

Auto-called on BLE connect. Logged to debug.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 17:29:38 +09:00
dwjang 549a2c722e feat: PlacementGuideView + MeasurementLogService + navigation flow
PlacementGuideView.kt:
- 6-sensor Canvas layout (CH0-CH3 vertical + CH4/CH5 lateral wings)
- Per-channel UrinAI detection (green/red color coding)
- Direction guidance (up/down/left/right)
- Check Position → maa → detect → display
- Start Measurement when 4+ channels detected

MeasurementLogService.kt:
- JSON Lines format, daily rotation
- Events: measurement, placement, ble_connected/disconnected, error
- 90-day auto-purge
- App internal storage: vesiscan_logs/YYYY-MM-DD.jsonl

AppState.kt + MainActivity.kt:
- PLACEMENT_GUIDE screen added to navigation
- Flow: personalization → placementGuide → piezoMonitoring

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 16:43:02 +09:00
dwjang c136d64665 feat: full Piezo measurement pipeline + waveform charts in PiezoMonitoringView
Major update matching iOS PiezoMonitoringView:

measure() — real BLE measurement via maa:
- sendAllChannels → piezoCollector → analyzeMultiChannel
- Build walls → estimateBladderVolume (Frustum)
- Auto-retry on timeout (max 2, 15s each)
- Volume-based bladder level update

UI additions:
- Max Volume info card (orange)
- Volume card shows measured value
- Developer Tools (collapsible):
  - 6-Channel Analysis with Measure button
  - Zone indicator (GOOD/PARTIAL/ADJUST)
  - Channel status bar (green/red, urine length)
  - Per-channel waveform charts (Canvas):
    - Raw (light blue) + Denoised (blue) overlay
    - Threshold line (1600, orange dashed)
    - Low-echo span (yellow)
    - Urine region (cyan) with ant(green)/post(red) markers
    - Y-axis 0-2500 fixed, X-axis depth in mm

resetMeasurement() on catheterize

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 16:38:33 +09:00
dwjang 444aa6280f fix: critical bug in PiezoPacketCollector processEndPacket
CRITICAL: In multi-channel mode, "ree:" packets between channels
were silently dropped instead of saving the current channel data.
This caused channel data loss when channels are separated by "ree:".

Fixed: Added missing isMultiChannel + ree: branch that calls
saveCurrentChannel() — matching Swift behavior exactly.

Found via line-by-line Swift vs Kotlin verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 16:21:43 +09:00
dwjang c1c6dfc16a feat: port algorithm core from iOS (4 files)
GreenZoneConstants.kt — all 16 constants (thresholds, physical)
UrinAI.kt — urine gatekeeper (binary threshold detection)
PiezoEchoAnalyzer.kt — low-echo detection with prominence-based wall selection
PiezoBVEstimator.kt — Frustum BV estimation with device presets

1:1 port from Swift, same logic, same constants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 16:14:07 +09:00
dwjang b83cb16f35 feat: add Piezo BLE data pipeline (1:1 port from iOS)
PiezoPacketCollector.kt:
- Multi-packet reb:/red:/raa: assembly
- Auto-detect Big/Little Endian
- First channel locks endianness for all channels
- 6-channel support (maa command)

BleManager.kt:
- sendAllChannels(mode) → maa command
- reb:/rer:/raa: response routing to piezoCollector
- piezoCollector instance added

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 16:06:42 +09:00
dwjang dc61e1d52e i18n: add Korean/English localization via strings.xml
Replace hardcoded Korean UI strings in Piezo, CatheterizeSheet,
VoidingDiary, and UrineCameraScreen with stringResource() calls.
Add values/strings.xml (English) and values-ko/strings.xml (Korean).
System language setting now controls app language.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 11:27:32 +09:00
dw.jang 0e186234d2 UX: 수기 입력 기본값을 현재 추정 용량(estimatedVolume)으로 설정
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 09:34:02 +09:00
dw.jang 0e13f2bbb6 feat: consent screen, reminder system, medical metrics, sex field, design tokens, code quality
A. Reminder: ReminderService (AlarmManager chain), ReminderSettingsView,
   bell icon in Piezo top bar, reschedule on catheterization
B. Consent: ConsentView (3 mandatory items), onboarding→consent→registration flow,
   persistent consent storage, sex field in User + Registration Step 2
C. Diary: 24hUV, NUV, NPI, max capacity, day/night frequency metrics card
D. Design: DesignTokens.kt, 48dp touch targets, DonutChart semantics, pressScale modifier
E. Quality: BuildConfig.DEBUG log wrapping, DateFormatter caching, background I/O for diary store

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 14:26:02 +09:00
dw.jang 1d5c6f15f8 UX: Piezo compact icon buttons 36dp → 44dp (minimum touch target)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:00:46 +09:00
dw.jang 65d5d81e9c UX fix: delete button touch target 24dp → 48dp (default IconButton)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 14:52:24 +09:00
dw.jang e142b55063 Fix camera screen: navigate via AppScreen instead of overlay
Camera was hidden behind PiezoMonitoringView because it was rendered
as a child composable. Now uses AppScreen.URINE_CAMERA for full
Crossfade navigation — camera gets its own screen transition.

Flow: Catheterize → 카메라 → AppScreen.URINE_CAMERA → measure → back to PIEZO_MONITORING

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 14:21:00 +09:00
dw.jang 333df201f9 Fix: use AlertDialog for input selection + direct voice/manual dialogs
Replace ModalBottomSheet-based flow with AlertDialog:
- Catheterize → AlertDialog with 3 buttons (수기/음성/카메라)
- 수기: AlertDialog with number TextField + save
- 음성: AlertDialog with auto-start SpeechRecognizer + auto-save
- 카메라: showUrineCamera directly

VoidingDiaryView: sort date groups descending (newest first)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 14:07:05 +09:00
dw.jang b4279d7698 Fix tab switching in CatheterizeSheet + camera launch timing
CatheterizeSheet:
- Replace OutlinedButton with filled Button for tab selection
- Selected tab: filled color, unselected: 10% opacity background
- Tabs now reliably switch between 수기/음성/카메라
- Camera tab directly calls onCameraRequested + onDismiss

PiezoMonitoringView:
- Fix camera launch: use pendingCamera + LaunchedEffect
  to ensure ModalBottomSheet fully closes before camera opens
- Prevents state race condition between sheet dismiss and camera show

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 13:06:38 +09:00
dw.jang ada507f4b4 Add voiding diary view + home icon navigation
VoidingDiaryView:
- Date-grouped record list (grouped by day)
- Shows volume/awake/sleep with icons and colors
- Method badge (수기/음성/카메라)
- Delete with confirmation dialog
- Total record count footer

Navigation changes:
- Piezo screen: ArrowBack → Home icon (집 아이콘)
- Piezo screen: 배뇨일지 icon (Description) added next to home
- NIRS screen: ArrowBack → Home icon
- AppScreen.VOIDING_DIARY route added to Crossfade navigation
- VoidingDiaryView navigates back to HOME

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 12:25:56 +09:00
dw.jang 78552ae784 Add urine cup camera screen with YOLO auto-capture + V2 measurement
UrineCameraScreen:
- YOLO real-time cup detection with green border indicator
- Auto-capture: 2s continuous detection (≥90%) triggers shutter
- Manual shutter button fallback
- V2 SimpleMeasureService analysis (OCR + 3-zone gradient)
- Auto-analyze after capture
- Result display with volume (ml)
- Save to local VoidingRecordStore + toast confirmation
- Retake option on failure
- Brightness guide text

PiezoMonitoringView:
- Camera tab in CatheterizeSheet now opens UrineCameraScreen
- Full-screen overlay for camera (doesn't leave Piezo screen)
- Auto bladder reset on successful measurement

Complete Catheterize flow:
  Catheterize → [수기/음성/카메라] → 입력 → "등록되었습니다" → 레벨 리셋

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 11:59:21 +09:00
dw.jang c15fcfccca Integrate voiding diary into Piezo catheterization flow
Catheterize button now opens a voiding record sheet with 3 input modes:
- Manual: number TextField (1-1000ml)
- Voice: SpeechRecognizer + VolumeParser (Korean/English)
- Camera: placeholder for YOLO cup measurement (TODO)

On save: "배뇨일지에 등록되었습니다" toast, bladder level resets to 0

New modules (from Uridiary):
- measure/: SimpleMeasureService, YoloDetector, CCPosition, VoidingRecord
- speech/: SpeechRecognizerManager, VolumeParser, HfVolumeExtractor
- assets/urinecup_best.onnx: YOLO model for cup detection

VoidingRecordStore: local SharedPreferences+JSON storage for voiding records

Dependencies added: ONNX Runtime, ML Kit OCR, CameraX, OkHttp
Permissions added: CAMERA, RECORD_AUDIO, INTERNET

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 11:47:59 +09:00
dw.jang 064f1d1f8d fix: disable dark mode and dynamic color to fix Samsung foldable display issues
Force light theme only, block Samsung Force Dark Mode via forceDarkAllowed=false,
and add explicit text colors to prevent invisible text on light backgrounds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 11:48:51 +09:00
dw.jang e6334f067a feat: add custom app icon with bladder drop, pulse wave, and BLE signal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 10:25:51 +09:00
dw.jang 76dbf5fe12 feat: responsive layout for foldable phones (compact < 400dp)
Reduce padding, icon sizes, and chart dimensions on narrow screens.
Hide title text and battery % in compact mode to prevent top bar
overflow. Scale donut chart 240→180dp and SpO2 text 56→40sp.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 10:36:53 +09:00
dw.jang 9feaae1939 feat: add demo mode for testing without BLE device
Add "Demo Mode" button in device scan screen that bypasses BLE
connection and calibration. NIRS monitoring generates simulated
random data at 4Hz. Both monitoring views show DEMO badge in top bar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 10:16:59 +09:00
dw.jang 99c58382b4 feat: add X/Y axis labels and grid lines to NIRS charts
Y-axis shows data range with 5 tick labels, X-axis shows elapsed time
in seconds, horizontal grid lines for readability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 14:21:16 +09:00
dw.jang 3c8de65dab fix: prevent BLE freeze on NIRS streaming stop
Clear mcj callback immediately on stop to prevent stale responses from
triggering new polling chains. Delay battery polling resume by 500ms to
avoid command collision with in-flight mcj response. Add sendNirsPowerOff
for future use.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 11:33:57 +09:00
dw.jang 254381dbaa feat: BLE stability fixes, NIRS streaming improvements, catheter count feature
- Fix BLE API deprecation warnings with API 33+ version checks
- Move isServiceReady to onDescriptorWrite for reliable CCCD setup
- Fix battery polling: immediate query on connect, pause during NIRS streaming
- Add mcj retry mechanism for NIRS streaming reliability
- Add debug logging to BLE, NIRS parsing, and processing pipeline
- Add catheter count feature to Piezo monitoring (persistent count, add dialog, auto-decrement on catheterize)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:26:36 +09:00
dw.jang 91c5e5e7e0 fix: NIRS connection failure 2026-03-09 17:36:47 +09:00
dw.jang a8532917b4 Initial commit: VesiscanBasic Android app
Port of MedilightV2 iOS app to Android with Kotlin + Jetpack Compose.
Includes BLE connectivity, NIRS/Piezo sensor modes, Rive animations,
interactive measurement history charts, and foldable device support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:27:00 +09:00