android: camera-style controls - 4-item top bar (ffc/zoom/trace/palette), shutter row (gallery/photo/record) above nav
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -59,7 +59,7 @@ fun AppRoot() {
|
|||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
when (tab) {
|
when (tab) {
|
||||||
0 -> LiveScreen()
|
0 -> LiveScreen(onOpenGallery = { tab = 1 })
|
||||||
1 -> Column(modifier = Modifier.fillMaxSize().padding(bottom = 84.dp)) {
|
1 -> Column(modifier = Modifier.fillMaxSize().padding(bottom = 84.dp)) {
|
||||||
GalleryScreen()
|
GalleryScreen()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,32 @@
|
|||||||
package com.mag160c.thermal.ui.live
|
package com.mag160c.thermal.ui.live
|
||||||
|
|
||||||
|
import android.view.SurfaceView
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
import androidx.compose.foundation.layout.only
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.safeDrawing
|
import androidx.compose.foundation.layout.safeDrawing
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -31,41 +39,47 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.layout.onSizeChanged
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.mag160c.thermal.R
|
import com.mag160c.thermal.R
|
||||||
import com.mag160c.thermal.core.Palettes
|
import com.mag160c.thermal.core.Palettes
|
||||||
import android.view.SurfaceView
|
import com.mag160c.thermal.ui.DeviceOrientation
|
||||||
|
import com.mag160c.thermal.ui.UiInsets
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stable single-branch live view: one SurfaceView for the whole screen, a
|
* Stable single-branch live view, camera-app style:
|
||||||
* control TOP bar (always visible) and the bottom navigation overlay.
|
* - control TOP bar (glued to the portrait top edge): FFC / zoom / max-temp
|
||||||
* The activity is portrait-locked, so the whole composition (top bar /
|
* trace / palette, each with a tiny label, pre-rotated by the grip angle;
|
||||||
* image region / bottom bar) is glued to the phone's portrait frame and
|
* - camera shutter row above the bottom navigation: gallery shortcut, big
|
||||||
* never moves or rotates, however the phone is physically held — the on-
|
* photo shutter, record/stop;
|
||||||
* screen area always matches the thermal lens direction. The bars' icons
|
* - the image region stays glued to the phone's portrait frame (matches the
|
||||||
* and text are pre-rotated by the physical grip angle (DeviceOrientation)
|
* lens direction) and never moves; OSD text compensates the grip angle.
|
||||||
* so they stay readable in any grip.
|
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun LiveScreen(vm: LiveViewModel = viewModel()) {
|
fun LiveScreen(vm: LiveViewModel = viewModel(), onOpenGallery: () -> Unit = {}) {
|
||||||
val state by vm.state.collectAsState()
|
val state by vm.state.collectAsState()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val phi by com.mag160c.thermal.ui.DeviceOrientation.deg.collectAsState()
|
val phi by DeviceOrientation.deg.collectAsState()
|
||||||
|
val density = LocalDensity.current.density
|
||||||
var showPalette by remember { mutableStateOf(false) }
|
var showPalette by remember { mutableStateOf(false) }
|
||||||
|
var navPx by remember { mutableStateOf(UiInsets.navPx) }
|
||||||
|
var shutterPx by remember { mutableStateOf(0) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
vm.connect()
|
vm.connect()
|
||||||
vm.uiBottomPx = com.mag160c.thermal.ui.UiInsets.navPx
|
vm.uiBottomPx = navPx + shutterPx
|
||||||
while (true) {
|
while (true) {
|
||||||
kotlinx.coroutines.delay(500)
|
kotlinx.coroutines.delay(400)
|
||||||
vm.uiBottomPx = com.mag160c.thermal.ui.UiInsets.navPx
|
navPx = UiInsets.navPx
|
||||||
|
vm.uiBottomPx = navPx + shutterPx
|
||||||
vm.refreshTemps()
|
vm.refreshTemps()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,7 +115,6 @@ fun LiveScreen(vm: LiveViewModel = viewModel()) {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.TopCenter)
|
.align(Alignment.TopCenter)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
// cutout / status-bar safe area (punch-hole phones)
|
|
||||||
.windowInsetsPadding(
|
.windowInsetsPadding(
|
||||||
WindowInsets.safeDrawing
|
WindowInsets.safeDrawing
|
||||||
.only(WindowInsetsSides.Top),
|
.only(WindowInsetsSides.Top),
|
||||||
@@ -111,49 +124,154 @@ fun LiveScreen(vm: LiveViewModel = viewModel()) {
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 4.dp),
|
.padding(vertical = 6.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Box(modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() }, contentAlignment = Alignment.Center) {
|
Box(
|
||||||
IconButton(onClick = { vm.triggerFfc() }) {
|
modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() },
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.clickable { vm.triggerFfc() }.padding(4.dp),
|
||||||
|
) {
|
||||||
Icon(painterResource(R.drawable.ic_ffc), "FFC 快门校正")
|
Icon(painterResource(R.drawable.ic_ffc), "FFC 快门校正")
|
||||||
|
Text("FFC", style = MaterialTheme.typography.labelSmall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box(modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() }, contentAlignment = Alignment.Center) {
|
Box(
|
||||||
IconButton(onClick = { vm.setZoom(vm.state.value.zoom % 4 + 1) }) {
|
modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() },
|
||||||
Icon(painterResource(R.drawable.ic_zoom), "数码变倍 x${state.zoom}")
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
vm.setZoom(vm.state.value.zoom % 4 + 1)
|
||||||
|
}.padding(4.dp),
|
||||||
|
) {
|
||||||
|
Icon(painterResource(R.drawable.ic_zoom), "数码变倍")
|
||||||
|
Text("${state.zoom}×", style = MaterialTheme.typography.labelSmall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box(modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() }, contentAlignment = Alignment.Center) {
|
Box(
|
||||||
Text(
|
modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() },
|
||||||
text = if (state.maxTraceOn) "追踪·开" else "追踪",
|
contentAlignment = Alignment.Center,
|
||||||
style = MaterialTheme.typography.labelMedium,
|
) {
|
||||||
color = if (state.maxTraceOn) MaterialTheme.colorScheme.primary
|
Column(
|
||||||
else MaterialTheme.colorScheme.onSurfaceVariant,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
modifier = Modifier.clickable { vm.toggleMaxTrace() },
|
modifier = Modifier.clickable { vm.toggleMaxTrace() }.padding(4.dp),
|
||||||
)
|
) {
|
||||||
|
val on = state.maxTraceOn
|
||||||
|
Icon(
|
||||||
|
painterResource(R.drawable.ic_target), "最高温追踪",
|
||||||
|
tint = if (on) MaterialTheme.colorScheme.primary
|
||||||
|
else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
if (on) "追踪·开" else "追踪",
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = if (on) MaterialTheme.colorScheme.primary
|
||||||
|
else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Box(modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() }, contentAlignment = Alignment.Center) {
|
Box(
|
||||||
IconButton(onClick = { showPalette = true }) {
|
modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() },
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.clickable { showPalette = true }.padding(4.dp),
|
||||||
|
) {
|
||||||
Icon(painterResource(R.drawable.ic_palette), "调色板")
|
Icon(painterResource(R.drawable.ic_palette), "调色板")
|
||||||
|
Text(
|
||||||
|
Palettes.NAMES[state.paletteIndex],
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box(modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() }, contentAlignment = Alignment.Center) {
|
}
|
||||||
IconButton(onClick = { vm.capturePhoto(context) }) {
|
}
|
||||||
Icon(painterResource(R.drawable.ic_camera), "拍照")
|
|
||||||
|
// ---- camera shutter row above the bottom navigation (live tab) ----
|
||||||
|
val recording = state.status == "recording"
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomCenter)
|
||||||
|
.offset(y = -(navPx / density).dp)
|
||||||
|
.onSizeChanged {
|
||||||
|
shutterPx = it.height
|
||||||
|
vm.uiBottomPx = navPx + shutterPx
|
||||||
|
}
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 10.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(52.dp, Alignment.CenterHorizontally),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.graphicsLayer { rotationZ = -phi.toFloat() },
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(52.dp)
|
||||||
|
.background(Color(0x59FFFFFF), CircleShape)
|
||||||
|
.clickable { onOpenGallery() },
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Icon(painterResource(R.drawable.ic_gallery), null, tint = Color.White)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
"相册", color = Color.White,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.graphicsLayer { rotationZ = -phi.toFloat() },
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(74.dp)
|
||||||
|
.border(4.dp, Color.White, CircleShape)
|
||||||
|
.padding(6.dp)
|
||||||
|
.background(Color.White, CircleShape)
|
||||||
|
.clickable { vm.capturePhoto(context) },
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
"拍照", color = Color.White,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.graphicsLayer { rotationZ = -phi.toFloat() },
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(52.dp)
|
||||||
|
.border(3.dp, Color(0xFFFF5252), CircleShape)
|
||||||
|
.clickable { vm.toggleRecording(context) },
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
if (recording) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(20.dp)
|
||||||
|
.background(Color(0xFFFF5252), RoundedCornerShape(4.dp)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.background(Color(0xFFFF5252), CircleShape),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box(modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() }, contentAlignment = Alignment.Center) {
|
Text(
|
||||||
IconButton(onClick = { vm.toggleRecording(context) }) {
|
if (recording) "停止" else "录像", color = Color.White,
|
||||||
Icon(painterResource(R.drawable.ic_record), "录像")
|
style = MaterialTheme.typography.labelSmall,
|
||||||
}
|
)
|
||||||
}
|
|
||||||
Box(modifier = Modifier.weight(1f).graphicsLayer { rotationZ = -phi.toFloat() }, contentAlignment = Alignment.Center) {
|
|
||||||
Text(
|
|
||||||
text = Palettes.NAMES[state.paletteIndex],
|
|
||||||
style = MaterialTheme.typography.labelMedium,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M5,12a7,7 0 1,0 14,0a7,7 0 1,0 -14,0z" android:strokeColor="#FF000000" android:strokeWidth="2.5" android:fillColor="#00000000" />
|
||||||
|
<path android:pathData="M12,1.5 L12,5.5" android:strokeColor="#FF000000" android:strokeWidth="2.5" />
|
||||||
|
<path android:pathData="M12,18.5 L12,22.5" android:strokeColor="#FF000000" android:strokeWidth="2.5" />
|
||||||
|
<path android:pathData="M1.5,12 L5.5,12" android:strokeColor="#FF000000" android:strokeWidth="2.5" />
|
||||||
|
<path android:pathData="M18.5,12 L22.5,12" android:strokeColor="#FF000000" android:strokeWidth="2.5" />
|
||||||
|
<path android:pathData="M10.9,12a1.1,1.1 0 1,0 2.2,0a1.1,1.1 0 1,0 -2.2,0z" android:fillColor="#FF000000" />
|
||||||
|
</vector>
|
||||||
Binary file not shown.
@@ -118,9 +118,10 @@ res/drawable/*.xml 自绘矢量图标(双弧圆等可靠几何图形
|
|||||||
- **图像恒 90°CW 绘制为 3:4 竖向**,填满可用区域(顶栏下~底导航上)。
|
- **图像恒 90°CW 绘制为 3:4 竖向**,填满可用区域(顶栏下~底导航上)。
|
||||||
- **文字/图标恒屏幕水平**(可读);标记文字位置自动跟随(probeToScreen 固定 90° 映射:
|
- **文字/图标恒屏幕水平**(可读);标记文字位置自动跟随(probeToScreen 固定 90° 映射:
|
||||||
`fx=1-sy/120, fy=sx/160`)。
|
`fx=1-sy/120, fy=sx/160`)。
|
||||||
- 顶栏=控制项(FFC/变倍/追踪·开/调色板/拍照/录像/调色板名),恒在顶部,`safeDrawing`
|
- 顶栏=4 个相机控制项(FFC / 变倍×N / 追踪·开 / 调色板+名称),恒在顶部并带小字
|
||||||
顶部inset 适配挖孔屏。
|
标签,`safeDrawing` 顶部inset 适配挖孔屏。
|
||||||
- 底栏=导航恒在底部。
|
- 底部(仅实时页)导航栏上方为**相机快门区**:相册快捷入口 / 大快门拍照 /
|
||||||
|
录像-停止;快门区高度并入 `vm.uiBottomPx`(= 导航高+快门区高)。
|
||||||
- 顶栏高度经 `onSizeChanged`→`vm.uiTopPx`;底导航高度经 `UiInsets.navPx`→`vm.uiBottomPx`,渲染器据此留白。
|
- 顶栏高度经 `onSizeChanged`→`vm.uiTopPx`;底导航高度经 `UiInsets.navPx`→`vm.uiBottomPx`,渲染器据此留白。
|
||||||
- 演示模式:无设备时用真实管线+DDT渲染合成帧(热块场景),demo温度用局部线性显示映射
|
- 演示模式:无设备时用真实管线+DDT渲染合成帧(热块场景),demo温度用局部线性显示映射
|
||||||
(真机温度走管线数学;绝对温度标定待真机)。
|
(真机温度走管线数学;绝对温度标定待真机)。
|
||||||
|
|||||||
@@ -166,6 +166,19 @@
|
|||||||
正持显示,90/180/270 补偿几何不变(preview_fix_pose*_v7.png)。
|
正持显示,90/180/270 补偿几何不变(preview_fix_pose*_v7.png)。
|
||||||
- [x] 单元测试全过;APK 已更新。
|
- [x] 单元测试全过;APK 已更新。
|
||||||
|
|
||||||
|
## 用户反馈修复 第八轮(2026-09-07,相机式按键布局)
|
||||||
|
|
||||||
|
- [x] **顶栏精简为 4 个相机控制项**(左→右,带小字标签,随持机朝向补偿旋转):
|
||||||
|
FFC 校正 / 数码变倍(显示当前 1×/2×/4×,点击循环)/ 最高温追踪开关(追踪·开,
|
||||||
|
高亮+新 ic_target 准星图标)/ 调色板(显示当前调色板名,点击弹出选择)。
|
||||||
|
- [x] **底部新增相机快门区**(仅实时页,位于底导航上方,白/红色调):
|
||||||
|
相册快捷入口(跳转媒体库页签)/ 大圆形快门=拍照 / 录像-停止(红圈,录像中
|
||||||
|
红圈内容变红色方块)。快门区高度并入渲染器底部 inset(vm.uiBottomPx =
|
||||||
|
导航高+快门区高),测温点点击映射同步排除该区域。
|
||||||
|
- [x] 底部导航(实时/相册/分析/设置)不变;快门区内容同样按持机朝向补偿旋转。
|
||||||
|
- [x] 模拟器实测:0°/90° 姿态布局、旋转补偿、图像区域均正确
|
||||||
|
(preview_ui_v8.png / preview_ui90_v8.png);单元测试全过;APK 已更新。
|
||||||
|
|
||||||
## 待办
|
## 待办
|
||||||
|
|
||||||
- 真机USB实测(温度绝对值标定、FFC/录像/MDT保存端到端)
|
- 真机USB实测(温度绝对值标定、FFC/录像/MDT保存端到端)
|
||||||
|
|||||||
Reference in New Issue
Block a user