UI: 앱 아이콘 교체 + Placement 힌트 텍스트 정리
- 앱 아이콘: 방광이.png로 교체 (webp→png, adaptive-icon 제거) - Placement: "Centering" → "Checking", "LR balanced. Final check" → "Final check" - 모든 방향 힌트에 화살표(↑↓←→) 추가 — phaseProtected 시 아이콘 추론 보장 - 디바운스 중에도 directionIcon은 최신 guideResult로 갱신 (화살표 누락 수정) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@@ -226,7 +226,7 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
val hint = directionHint.ifEmpty { " " }
|
val hint = directionHint.ifEmpty { " " }
|
||||||
append(hint)
|
append(hint)
|
||||||
if (hint.startsWith("Slide") || hint.startsWith("Optimizing") ||
|
if (hint.startsWith("Slide") || hint.startsWith("Optimizing") ||
|
||||||
hint.startsWith("Centering") || hint.startsWith("Adjust")) {
|
hint.startsWith("Checking") || hint.startsWith("Adjust")) {
|
||||||
append(dots)
|
append(dots)
|
||||||
}
|
}
|
||||||
if (appState.isDevMode && !lastCv.isNaN()) {
|
if (appState.isDevMode && !lastCv.isNaN()) {
|
||||||
@@ -685,7 +685,17 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
} else if (phaseProtected && phaseHoldHint.isNotEmpty()) {
|
} else if (phaseProtected && phaseHoldHint.isNotEmpty()) {
|
||||||
// 보호 중 → 전환 힌트 강제 유지, lastHintChangeMs 안 건드림
|
// 보호 중 → 전환 힌트 강제 유지, lastHintChangeMs 안 건드림
|
||||||
directionHint = phaseHoldHint
|
directionHint = phaseHoldHint
|
||||||
directionIcon = "checkmark.circle"
|
// 힌트 텍스트에서 방향 아이콘 추론 (화살표 유지)
|
||||||
|
directionIcon = when {
|
||||||
|
phaseHoldHint.contains("up", ignoreCase = true) || phaseHoldHint.contains("↑") -> "arrow.up"
|
||||||
|
phaseHoldHint.contains("down", ignoreCase = true) || phaseHoldHint.contains("↓") -> "arrow.down"
|
||||||
|
phaseHoldHint.contains("left", ignoreCase = true) || phaseHoldHint.contains("←") -> "arrow.left"
|
||||||
|
phaseHoldHint.contains("right", ignoreCase = true) || phaseHoldHint.contains("→") -> "arrow.right"
|
||||||
|
else -> "checkmark.circle"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 디바운스 중이라도 화살표 방향은 최신 상태로 갱신
|
||||||
|
directionIcon = guideResult.icon
|
||||||
}
|
}
|
||||||
|
|
||||||
// GREEN hold 중이면 힌트 강제 유지
|
// GREEN hold 중이면 힌트 강제 유지
|
||||||
@@ -1234,7 +1244,7 @@ private fun computePlacementGuide(
|
|||||||
val detectedCount = detected.count { it }
|
val detectedCount = detected.count { it }
|
||||||
|
|
||||||
if (detectedCount == 0) {
|
if (detectedCount == 0) {
|
||||||
return PlacementGuideResult("Slide up", "arrow.up", 0,
|
return PlacementGuideResult("Slide up ↑", "arrow.up", 0,
|
||||||
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
|
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1292,7 +1302,7 @@ private fun computePlacementGuide(
|
|||||||
val lrHint = when {
|
val lrHint = when {
|
||||||
l4 > 0 && l5 > 0 && l4 > l5 -> "Slide left ←"
|
l4 > 0 && l5 > 0 && l4 > l5 -> "Slide left ←"
|
||||||
l4 > 0 && l5 > 0 && l5 > l4 -> "Slide right →"
|
l4 > 0 && l5 > 0 && l5 > l4 -> "Slide right →"
|
||||||
else -> "Centering"
|
else -> "Checking"
|
||||||
}
|
}
|
||||||
val lrIcon = when {
|
val lrIcon = when {
|
||||||
l4 > 0 && l5 > 0 && l4 > l5 -> "arrow.left"
|
l4 > 0 && l5 > 0 && l4 > l5 -> "arrow.left"
|
||||||
@@ -1328,13 +1338,13 @@ private fun computePlacementGuide(
|
|||||||
val hint: String; val icon: String
|
val hint: String; val icon: String
|
||||||
when {
|
when {
|
||||||
(ch0det || ch1det) && !ch2det && !ch3det -> {
|
(ch0det || ch1det) && !ch2det && !ch3det -> {
|
||||||
hint = "Slide up"; icon = "arrow.up"
|
hint = "Slide up ↑"; icon = "arrow.up"
|
||||||
}
|
}
|
||||||
(ch3det || ch2det) && !ch0det && !ch1det -> {
|
(ch3det || ch2det) && !ch0det && !ch1det -> {
|
||||||
hint = "Slide down"; icon = "arrow.down"
|
hint = "Slide down ↓"; icon = "arrow.down"
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
hint = "Slide up"; icon = "arrow.up"
|
hint = "Slide up ↑"; icon = "arrow.up"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return PlacementGuideResult(hint, icon, centerDetected * 10,
|
return PlacementGuideResult(hint, icon, centerDetected * 10,
|
||||||
@@ -1379,7 +1389,7 @@ private fun computePlacementGuide(
|
|||||||
val ch5ok = detected.getOrElse(5) { false }
|
val ch5ok = detected.getOrElse(5) { false }
|
||||||
|
|
||||||
// 항상 LATERAL 단계를 거침 — 전환 시 항상 Checking 표시
|
// 항상 LATERAL 단계를 거침 — 전환 시 항상 Checking 표시
|
||||||
val lrHint = "Centering"
|
val lrHint = "Checking"
|
||||||
val lrIcon = "checkmark.circle"
|
val lrIcon = "checkmark.circle"
|
||||||
val score = (40 + ((1.0 - kotlin.math.abs(gradient) / gradientThr) * 20).toInt())
|
val score = (40 + ((1.0 - kotlin.math.abs(gradient) / gradientThr) * 20).toInt())
|
||||||
.coerceIn(40, 60)
|
.coerceIn(40, 60)
|
||||||
@@ -1396,16 +1406,16 @@ private fun computePlacementGuide(
|
|||||||
val ch4ok = detected.getOrElse(4) { false }
|
val ch4ok = detected.getOrElse(4) { false }
|
||||||
val ch5ok = detected.getOrElse(5) { false }
|
val ch5ok = detected.getOrElse(5) { false }
|
||||||
// 항상 LATERAL 단계를 거침
|
// 항상 LATERAL 단계를 거침
|
||||||
return PlacementGuideResult("Centering",
|
return PlacementGuideResult("Checking",
|
||||||
"checkmark.circle", 50,
|
"checkmark.circle", 50,
|
||||||
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.LATERAL)
|
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.LATERAL)
|
||||||
} else if (gradient > 0) {
|
} else if (gradient > 0) {
|
||||||
val intensity = if (gradient > 0.8) "Slide down" else "Slide down slightly"
|
val intensity = if (gradient > 0.8) "Slide down ↓" else "Slide down slightly ↓"
|
||||||
return PlacementGuideResult(intensity, "arrow.down",
|
return PlacementGuideResult(intensity, "arrow.down",
|
||||||
(20 + centerDetected * 5).coerceAtMost(35),
|
(20 + centerDetected * 5).coerceAtMost(35),
|
||||||
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
|
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
|
||||||
} else {
|
} else {
|
||||||
val intensity = if (gradient < -0.8) "Slide up" else "Slide up slightly"
|
val intensity = if (gradient < -0.8) "Slide up ↑" else "Slide up slightly ↑"
|
||||||
return PlacementGuideResult(intensity, "arrow.up",
|
return PlacementGuideResult(intensity, "arrow.up",
|
||||||
(20 + centerDetected * 5).coerceAtMost(35),
|
(20 + centerDetected * 5).coerceAtMost(35),
|
||||||
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
|
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
|
||||||
@@ -1428,7 +1438,7 @@ private fun computePlacementGuide(
|
|||||||
// 절대 차이 10 sample 이하 → 좌우 충분히 균등, 바로 GREEN
|
// 절대 차이 10 sample 이하 → 좌우 충분히 균등, 바로 GREEN
|
||||||
if (ch4det && ch5det && kotlin.math.abs(len4 - len5) <= 10) {
|
if (ch4det && ch5det && kotlin.math.abs(len4 - len5) <= 10) {
|
||||||
return PlacementGuideResult(
|
return PlacementGuideResult(
|
||||||
"LR balanced. Final check...",
|
"Final check...",
|
||||||
"checkmark.circle", 60,
|
"checkmark.circle", 60,
|
||||||
phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.GREEN)
|
phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.GREEN)
|
||||||
}
|
}
|
||||||
@@ -1582,10 +1592,10 @@ private fun computePlacementGuide(
|
|||||||
adjustHint = "Slide right →"; adjustIcon = "arrow.right"
|
adjustHint = "Slide right →"; adjustIcon = "arrow.right"
|
||||||
}
|
}
|
||||||
gradient > 0.3 -> {
|
gradient > 0.3 -> {
|
||||||
adjustHint = "Slide down"; adjustIcon = "arrow.down"
|
adjustHint = "Slide down ↓"; adjustIcon = "arrow.down"
|
||||||
}
|
}
|
||||||
gradient < -0.3 -> {
|
gradient < -0.3 -> {
|
||||||
adjustHint = "Slide up"; adjustIcon = "arrow.up"
|
adjustHint = "Slide up ↑"; adjustIcon = "arrow.up"
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// 방향 애매 + CV 미달 → ch별 len 분석으로 방향 제시
|
// 방향 애매 + CV 미달 → ch별 len 분석으로 방향 제시
|
||||||
@@ -1596,10 +1606,10 @@ private fun computePlacementGuide(
|
|||||||
// 최소 채널이 최대의 60% 미만 → 약한 쪽으로 안내
|
// 최소 채널이 최대의 60% 미만 → 약한 쪽으로 안내
|
||||||
if (minCh <= 1) {
|
if (minCh <= 1) {
|
||||||
// ch0/ch1 약함 → 위로
|
// ch0/ch1 약함 → 위로
|
||||||
adjustHint = "Slide up slightly"; adjustIcon = "arrow.up"
|
adjustHint = "Slide up slightly ↑"; adjustIcon = "arrow.up"
|
||||||
} else {
|
} else {
|
||||||
// ch2/ch3 약함 → 아래로
|
// ch2/ch3 약함 → 아래로
|
||||||
adjustHint = "Slide down slightly"; adjustIcon = "arrow.down"
|
adjustHint = "Slide down slightly ↓"; adjustIcon = "arrow.down"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
adjustHint = "Optimizing"; adjustIcon = "checkmark.circle"
|
adjustHint = "Optimizing"; adjustIcon = "checkmark.circle"
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
|
||||||
</adaptive-icon>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
|
||||||
</adaptive-icon>
|
|
||||||
|
After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |