refactor: 데모 브랜치 패키지 통일 com.example.medilightv2android → com.medithings.vesiscan
사용자앱 (feature/tab-navigation) 과 패키지 이름 일치. namespace + applicationId 둘 다 com.medithings.vesiscan 로 변경 (기존 데모 앱은 재설치 필요). ## 변경 범위 - Kotlin 148 파일: package + import 문 (714 occurrences) - 디렉토리 이동: com/example/medilightv2android → com/medithings/vesiscan (main, test, androidTest 각각) - app/build.gradle.kts: namespace, applicationId - docs/FLAVOR_DEMO_STABLE.md: 참조 갱신 - V41DetectorCH4Test: BvDispatchResult.methodChosen → method (dto field name fix) ## 주의 - applicationId 가 바뀌므로 기존 데모 앱 (com.example.medilightv2android.demo) 은 Android 관점에서 다른 앱으로 취급 — 재설치 시 PIN/설정 초기화됨. - Fresh install 권장. 기존 앱 (com.example...) 은 별도로 uninstall 필요. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
package com.medithings.vesiscan
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import app.rive.runtime.kotlin.core.Rive
|
||||
import com.medithings.vesiscan.services.KeychainService
|
||||
import com.medithings.vesiscan.services.NotificationService
|
||||
import com.medithings.vesiscan.R
|
||||
import com.medithings.vesiscan.ui.theme.Medilightv2androidTheme
|
||||
import com.medithings.vesiscan.ui.views.calibration.CalibrationView
|
||||
import com.medithings.vesiscan.ui.views.connection.DeviceScanView
|
||||
import com.medithings.vesiscan.ui.views.history.MeasurementHistoryView
|
||||
import com.medithings.vesiscan.ui.views.home.HomeView
|
||||
import com.medithings.vesiscan.ui.views.monitoring.PlacementGuideView
|
||||
import com.medithings.vesiscan.ui.views.monitoring.PiezoMonitoringView
|
||||
import com.medithings.vesiscan.ui.views.monitoring.VivamyoMonitoringView
|
||||
import com.medithings.vesiscan.ui.views.monitoring.UrineCameraScreen
|
||||
import com.medithings.vesiscan.ui.views.monitoring.VoidingDiaryView
|
||||
import com.medithings.vesiscan.ui.views.onboarding.OnboardingView
|
||||
import com.medithings.vesiscan.ui.views.personalization.PiezoPersonalizationView
|
||||
import com.medithings.vesiscan.ui.views.pin.PinMode
|
||||
import com.medithings.vesiscan.ui.views.pin.PinView
|
||||
import com.medithings.vesiscan.ui.views.registration.RegistrationView
|
||||
import com.medithings.vesiscan.ui.views.consent.ConsentView
|
||||
import com.medithings.vesiscan.ui.views.reminder.ReminderSettingsView
|
||||
import com.medithings.vesiscan.ui.views.sensorselect.SensorSelectView
|
||||
import com.medithings.vesiscan.ui.views.clinical.ClinicalHomeView
|
||||
import com.medithings.vesiscan.ui.views.clinical.ClinicalLiveView
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
/** 사용자가 선택한 앱 언어 적용 — Activity 생성 전에 Locale wrap. */
|
||||
override fun attachBaseContext(newBase: android.content.Context) {
|
||||
super.attachBaseContext(com.medithings.vesiscan.services.LocalePref.wrap(newBase))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Initialize services
|
||||
Rive.init(this)
|
||||
KeychainService.init(this)
|
||||
NotificationService.getInstance(this)
|
||||
|
||||
enableEdgeToEdge()
|
||||
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
setContent {
|
||||
Medilightv2androidTheme {
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
MedilightApp(modifier = Modifier.padding(innerPadding))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MedilightApp(modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
val appState = remember { AppState(context) }
|
||||
|
||||
// HOME에서 뒤로가기 → 앱 종료 확인
|
||||
var showExitDialog by remember { mutableStateOf(false) }
|
||||
BackHandler(enabled = appState.currentScreen == AppScreen.HOME) {
|
||||
showExitDialog = true
|
||||
}
|
||||
if (showExitDialog) {
|
||||
androidx.compose.material3.AlertDialog(
|
||||
onDismissRequest = { showExitDialog = false },
|
||||
title = { androidx.compose.material3.Text(stringResource(R.string.exit_app_title)) },
|
||||
text = { androidx.compose.material3.Text(stringResource(R.string.exit_app_message)) },
|
||||
confirmButton = {
|
||||
androidx.compose.material3.TextButton(onClick = { (context as? android.app.Activity)?.finish() }) {
|
||||
androidx.compose.material3.Text(stringResource(R.string.exit_app_confirm), color = Color.Red)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
androidx.compose.material3.TextButton(onClick = { showExitDialog = false }) {
|
||||
androidx.compose.material3.Text(stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 다른 화면에서 뒤로가기 = 바로 전 화면
|
||||
BackHandler(enabled = appState.currentScreen != AppScreen.HOME
|
||||
&& appState.currentScreen != AppScreen.PIN_ENTRY) {
|
||||
when (appState.currentScreen) {
|
||||
// 메인 플로우: Home → DeviceScan → Personalization → Placement → Monitoring
|
||||
// DeviceScan Back: 임상 흐름이면 ClinicalHome으로, 아니면 HOME
|
||||
AppScreen.DEVICE_SCAN ->
|
||||
appState.currentScreen =
|
||||
if (com.medithings.vesiscan.services.ClinicalSessionStore.inClinicalFlow)
|
||||
AppScreen.CLINICAL_HOME
|
||||
else AppScreen.HOME
|
||||
AppScreen.PIEZO_PERSONALIZATION -> appState.currentScreen = AppScreen.DEVICE_SCAN
|
||||
// 임상 모드 측정 중이면 endMeasurement → ClinicalHome 복귀
|
||||
AppScreen.PLACEMENT_GUIDE ->
|
||||
if (com.medithings.vesiscan.services.ClinicalSessionStore.currentSession != null) {
|
||||
com.medithings.vesiscan.services.ClinicalSessionStore.endMeasurement(context)
|
||||
appState.currentScreen = AppScreen.CLINICAL_HOME
|
||||
} else appState.backFromPlacementGuide()
|
||||
AppScreen.PIEZO_MONITORING ->
|
||||
if (com.medithings.vesiscan.services.ClinicalSessionStore.currentSession != null) {
|
||||
com.medithings.vesiscan.services.ClinicalSessionStore.endMeasurement(context)
|
||||
appState.currentScreen = AppScreen.CLINICAL_HOME
|
||||
} else appState.currentScreen = AppScreen.PLACEMENT_GUIDE
|
||||
AppScreen.CLINICAL_HOME -> {
|
||||
com.medithings.vesiscan.services.ClinicalSessionStore.inClinicalFlow = false
|
||||
appState.currentScreen = AppScreen.HOME
|
||||
}
|
||||
AppScreen.CLINICAL_LIVE -> {
|
||||
// 측정 폴더 finalize 후 ClinicalHome 복귀
|
||||
com.medithings.vesiscan.services.ClinicalSessionStore.endMeasurement(context)
|
||||
appState.currentScreen = AppScreen.CLINICAL_HOME
|
||||
}
|
||||
// 모니터링 하위 화면 → 모니터링
|
||||
AppScreen.MEASUREMENT_HISTORY -> appState.currentScreen = AppScreen.PIEZO_MONITORING
|
||||
AppScreen.VOIDING_DIARY -> appState.currentScreen = AppScreen.PIEZO_MONITORING
|
||||
AppScreen.REMINDER_SETTINGS -> appState.currentScreen = AppScreen.PIEZO_MONITORING
|
||||
AppScreen.URINE_CAMERA -> appState.currentScreen = AppScreen.PIEZO_MONITORING
|
||||
// 기타
|
||||
AppScreen.SENSOR_SELECT -> appState.currentScreen = AppScreen.HOME
|
||||
AppScreen.VIVAMYO_MONITORING -> appState.currentScreen = AppScreen.HOME
|
||||
AppScreen.NIRS_CALIBRATION -> appState.currentScreen = AppScreen.DEVICE_SCAN
|
||||
// 온보딩/등록 (뒤로가기 막지 않되 Home으로)
|
||||
else -> appState.currentScreen = AppScreen.HOME
|
||||
}
|
||||
}
|
||||
|
||||
Crossfade(
|
||||
targetState = appState.currentScreen,
|
||||
animationSpec = tween(300),
|
||||
modifier = modifier,
|
||||
label = "screen"
|
||||
) { screen ->
|
||||
when (screen) {
|
||||
AppScreen.ONBOARDING -> OnboardingView(appState)
|
||||
AppScreen.CONSENT -> ConsentView(appState)
|
||||
AppScreen.REGISTRATION -> RegistrationView(appState)
|
||||
AppScreen.HOME -> HomeView(appState)
|
||||
AppScreen.SENSOR_SELECT -> SensorSelectView(appState)
|
||||
AppScreen.DEVICE_SCAN -> DeviceScanView(appState)
|
||||
AppScreen.NIRS_CALIBRATION -> CalibrationView(appState)
|
||||
AppScreen.PIEZO_PERSONALIZATION -> PiezoPersonalizationView(appState)
|
||||
AppScreen.PLACEMENT_GUIDE -> PlacementGuideView(appState)
|
||||
AppScreen.PIN_SETUP -> PinView(appState, PinMode.SETUP, context)
|
||||
AppScreen.PIN_ENTRY -> PinView(appState, PinMode.ENTER, context)
|
||||
AppScreen.VIVAMYO_MONITORING -> VivamyoMonitoringView(appState)
|
||||
AppScreen.PIEZO_MONITORING -> PiezoMonitoringView(appState)
|
||||
AppScreen.MEASUREMENT_HISTORY -> MeasurementHistoryView(appState)
|
||||
AppScreen.VOIDING_DIARY -> VoidingDiaryView(onBack = { appState.currentScreen = AppScreen.HOME })
|
||||
AppScreen.URINE_CAMERA -> UrineCameraScreen(
|
||||
onDone = { volume ->
|
||||
if (volume != null && volume > 0) appState.resetLevel()
|
||||
appState.currentScreen = AppScreen.PIEZO_MONITORING
|
||||
},
|
||||
onCancel = { appState.currentScreen = AppScreen.PIEZO_MONITORING }
|
||||
)
|
||||
AppScreen.REMINDER_SETTINGS -> ReminderSettingsView(
|
||||
onBack = { appState.backToMonitoring() }
|
||||
)
|
||||
AppScreen.CLINICAL_HOME -> ClinicalHomeView(appState)
|
||||
AppScreen.CLINICAL_LIVE -> ClinicalLiveView(appState)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user