feat: UI 일괄 수정 (5,6,7,8번)
5. Onboarding 간소화: 4단계→1단계 (Max Volume만) Step 1(자세)/2(위치)/4(최적화) 제거, Continue 바로 완료 6. 자동 reconnect: 앱 시작 시 마지막 기기에 자동 연결 SharedPreferences에 address+name 저장, HomeView에서 autoConnectLastDevice() 7. Demo 모드: DeviceScanView에서 Demo 버튼 제거 8. Android 뒤로가기: BackHandler로 화면별 네비게이션 매핑 Placement→Home, Monitoring→Placement, History/Diary→Monitoring 등 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package com.example.medilightv2android
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
@@ -60,6 +61,23 @@ fun MedilightApp(modifier: Modifier = Modifier) {
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val appState = remember { AppState(context) }
|
val appState = remember { AppState(context) }
|
||||||
|
|
||||||
|
// Android 뒤로가기 = 앱 내 뒤로가기
|
||||||
|
BackHandler(enabled = appState.currentScreen != AppScreen.HOME) {
|
||||||
|
when (appState.currentScreen) {
|
||||||
|
AppScreen.SENSOR_SELECT -> appState.currentScreen = AppScreen.HOME
|
||||||
|
AppScreen.DEVICE_SCAN -> appState.currentScreen = AppScreen.SENSOR_SELECT
|
||||||
|
AppScreen.PIEZO_PERSONALIZATION -> appState.currentScreen = AppScreen.DEVICE_SCAN
|
||||||
|
AppScreen.PLACEMENT_GUIDE -> appState.goHome()
|
||||||
|
AppScreen.PIEZO_MONITORING -> appState.currentScreen = AppScreen.PLACEMENT_GUIDE
|
||||||
|
AppScreen.VIVAMYO_MONITORING -> appState.goHome()
|
||||||
|
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
|
||||||
|
else -> appState.currentScreen = AppScreen.HOME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Crossfade(
|
Crossfade(
|
||||||
targetState = appState.currentScreen,
|
targetState = appState.currentScreen,
|
||||||
animationSpec = tween(300),
|
animationSpec = tween(300),
|
||||||
|
|||||||
@@ -221,6 +221,17 @@ class BleManager private constructor(private val context: Context) {
|
|||||||
handler.postDelayed(timeout, 10000)
|
handler.postDelayed(timeout, 10000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 저장된 마지막 기기에 자동 연결 시도 */
|
||||||
|
fun autoConnectLastDevice() {
|
||||||
|
if (isConnected.value) return
|
||||||
|
val prefs = context.getSharedPreferences("medilight_prefs", Context.MODE_PRIVATE)
|
||||||
|
val address = prefs.getString("last_device_address", null) ?: return
|
||||||
|
val name = prefs.getString("last_device_name", null) ?: return
|
||||||
|
debugLogger.info("AUTO_CONNECT attempting $name ($address)")
|
||||||
|
lastConnectedAddress = address
|
||||||
|
scheduleAutoReconnect()
|
||||||
|
}
|
||||||
|
|
||||||
fun disconnect() {
|
fun disconnect() {
|
||||||
isUserDisconnect = true
|
isUserDisconnect = true
|
||||||
cancelAutoReconnect()
|
cancelAutoReconnect()
|
||||||
@@ -614,7 +625,10 @@ class BleManager private constructor(private val context: Context) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
val prefs = context.getSharedPreferences("medilight_prefs", Context.MODE_PRIVATE)
|
val prefs = context.getSharedPreferences("medilight_prefs", Context.MODE_PRIVATE)
|
||||||
prefs.edit().putString("last_device_address", gatt.device.address).apply()
|
prefs.edit()
|
||||||
|
.putString("last_device_address", gatt.device.address)
|
||||||
|
.putString("last_device_name", connectedDeviceName.value)
|
||||||
|
.apply()
|
||||||
} catch (_: Exception) {}
|
} catch (_: Exception) {}
|
||||||
|
|
||||||
gatt.discoverServices()
|
gatt.discoverServices()
|
||||||
|
|||||||
-10
@@ -249,16 +249,6 @@ fun DeviceScanView(appState: AppState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Demo mode button
|
|
||||||
TextButton(
|
|
||||||
onClick = { appState.enterDemoMode() },
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 24.dp)
|
|
||||||
) {
|
|
||||||
Text("Demo Mode (No Device)", fontSize = 14.sp, color = MlSecondaryText)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan button
|
// Scan button
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import androidx.compose.material3.Button
|
|||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import com.example.medilightv2android.ble.BleManager
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -22,6 +25,12 @@ import com.example.medilightv2android.ui.theme.*
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeView(appState: AppState) {
|
fun HomeView(appState: AppState) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
// 앱 시작 시 마지막 기기에 자동 연결 시도
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
BleManager.getInstance(context).autoConnectLastDevice()
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|||||||
+4
-32
@@ -37,7 +37,7 @@ fun PiezoPersonalizationView(appState: AppState) {
|
|||||||
val handler = remember { Handler(Looper.getMainLooper()) }
|
val handler = remember { Handler(Looper.getMainLooper()) }
|
||||||
var currentStep by remember { mutableIntStateOf(1) }
|
var currentStep by remember { mutableIntStateOf(1) }
|
||||||
var optimizationComplete by remember { mutableStateOf(false) }
|
var optimizationComplete by remember { mutableStateOf(false) }
|
||||||
val totalSteps = 4
|
val totalSteps = 1
|
||||||
|
|
||||||
fun startOptimization() {
|
fun startOptimization() {
|
||||||
bleManager.onPiezoDataReceived = {
|
bleManager.onPiezoDataReceived = {
|
||||||
@@ -88,43 +88,15 @@ fun PiezoPersonalizationView(appState: AppState) {
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
// Step content
|
// Max Volume만 표시
|
||||||
when (currentStep) {
|
when (currentStep) {
|
||||||
1 -> PostureStep(appState)
|
1 -> MaxVolumeStep(appState)
|
||||||
2 -> SensorPositionStep(appState)
|
|
||||||
3 -> MaxVolumeStep(appState)
|
|
||||||
4 -> {
|
|
||||||
LaunchedEffect(Unit) { startOptimization() }
|
|
||||||
OptimizationStep(optimizationComplete)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
// Navigation button
|
// Navigation button
|
||||||
if (currentStep < 4) {
|
if (currentStep <= totalSteps) {
|
||||||
Button(
|
|
||||||
onClick = { currentStep++ },
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 32.dp)
|
|
||||||
.padding(bottom = 32.dp)
|
|
||||||
.height(56.dp),
|
|
||||||
shape = RoundedCornerShape(16.dp),
|
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent),
|
|
||||||
contentPadding = PaddingValues()
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.fillMaxSize().background(
|
|
||||||
Brush.horizontalGradient(listOf(MlTeal, MlPrimary)),
|
|
||||||
RoundedCornerShape(16.dp)
|
|
||||||
),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Text("Next", fontSize = 18.sp, fontWeight = FontWeight.SemiBold, color = Color.White)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (optimizationComplete) {
|
|
||||||
Button(
|
Button(
|
||||||
onClick = { appState.personalizationComplete() },
|
onClick = { appState.personalizationComplete() },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
Reference in New Issue
Block a user