Compare commits

..
4 Commits
15 changed files with 624 additions and 2 deletions
@@ -12,6 +12,7 @@ import androidx.compose.material3.Text
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.placeholder.ringhealth.core.designsystem.RingHealthTheme
/** 唯一 Activity,承载 Compose 界面。 */ /** 唯一 Activity,承载 Compose 界面。 */
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@@ -19,8 +20,11 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
MaterialTheme { RingHealthTheme {
Surface(modifier = Modifier.fillMaxSize()) { Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
// 文案取自 res/values/strings.xml,不在 Kotlin 里硬编码用户可见文字 // 文案取自 res/values/strings.xml,不在 Kotlin 里硬编码用户可见文字
Text(stringResource(R.string.app_name)) Text(stringResource(R.string.app_name))
@@ -0,0 +1,55 @@
package com.placeholder.ringhealth.core.common
import androidx.annotation.StringRes
import com.placeholder.ringhealth.R
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
/** 步数:千分位,如 8,426。 */
fun formatSteps(steps: Int): String = String.format(Locale.CHINA, "%,d", steps)
/** 距离:保留两位小数(单位 km 由 UI 单独展示)。 */
fun formatDistance(km: Double): String = String.format(Locale.CHINA, "%.2f", km)
/** 卡路里:只做千分位,单位「千卡」由 UI 用字符串资源拼接(ledger Ruling R13)。 */
fun formatCalories(kcal: Int): String = String.format(Locale.CHINA, "%,d", kcal)
/** 睡眠时长:7小时32分 / 45分 / 8小时。 */
fun formatSleepDuration(minutes: Int): String {
val hours = minutes / 60
val mins = minutes % 60
return when {
hours > 0 && mins > 0 -> "${hours}小时${mins}分"
hours > 0 -> "${hours}小时"
else -> "${mins}分"
}
}
/** 运动时长:1小时05分 / 42分(分钟补零)。 */
fun formatDuration(minutes: Int): String {
val hours = minutes / 60
val mins = minutes % 60
return if (hours > 0) {
String.format(Locale.CHINA, "%d小时%02d分", hours, mins)
} else {
"${mins}分"
}
}
/** 时间戳 → HH:mm。 */
fun formatClock(timestamp: Long): String =
SimpleDateFormat("HH:mm", Locale.CHINA).format(Date(timestamp))
/** 时间戳 → yyyy-MM-dd。 */
fun formatDate(timestamp: Long): String =
SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(timestamp))
/** 压力值 → 字符串资源 id。中文描述的唯一来源是 `strings.xml`(ledger Ruling R13)。 */
@StringRes
fun stressLevelLabelRes(stress: Int): Int = when {
stress < 30 -> R.string.stress_relaxed
stress < 60 -> R.string.stress_normal
stress < 80 -> R.string.stress_medium
else -> R.string.stress_high
}
@@ -0,0 +1,23 @@
package com.placeholder.ringhealth.core.designsystem
import androidx.compose.ui.graphics.Color
/** 品牌主色(健康绿,仿小米运动健康首页大环)。 */
val BrandGreen = Color(0xFF34C759)
val BrandGreenDark = Color(0xFF248A3D)
/** 页面与卡片背景。 */
val AppBackground = Color(0xFFF5F6F8)
val CardSurface = Color(0xFFFFFFFF)
val TextPrimary = Color(0xFF1C1C1E)
val TextSecondary = Color(0xFF8E8E93)
val DividerColor = Color(0xFFEFEFF4)
/** 各健康指标的语义色,全应用统一引用。 */
val MetricGreen = Color(0xFF34C759) // 步数
val MetricRed = Color(0xFFFF3B30) // 心率
val MetricBlue = Color(0xFF3B5BFF) // 睡眠
val MetricCyan = Color(0xFF00C7BE) // 血氧
val MetricPurple = Color(0xFFAF52DE) // 压力
val MetricOrange = Color(0xFFFF9500) // 体温
val MetricYellow = Color(0xFFFFCC00) // 血压 / 提醒
@@ -0,0 +1,28 @@
package com.placeholder.ringhealth.core.designsystem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
private val RingHealthColors = lightColorScheme(
primary = BrandGreen,
onPrimary = androidx.compose.ui.graphics.Color.White,
secondary = BrandGreenDark,
background = AppBackground,
onBackground = TextPrimary,
surface = CardSurface,
onSurface = TextPrimary,
surfaceVariant = DividerColor,
)
/**
* 应用主题。阶段一仅提供浅色模式(深色留到 M5)。
*/
@Composable
fun RingHealthTheme(content: @Composable () -> Unit) {
MaterialTheme(
colorScheme = RingHealthColors,
typography = RingHealthTypography,
content = content,
)
}
@@ -0,0 +1,23 @@
package com.placeholder.ringhealth.core.designsystem
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
/** 大号数值样式:首页大环与详情页主数字。 */
val MetricValueStyle = TextStyle(
fontSize = 40.sp,
fontWeight = FontWeight.Bold,
color = TextPrimary,
)
/** 中等数值样式:指标卡主数字。 */
val MetricTileValueStyle = TextStyle(
fontSize = 22.sp,
fontWeight = FontWeight.SemiBold,
color = TextPrimary,
)
/** 全局字体排版。 */
val RingHealthTypography = Typography()
@@ -0,0 +1,42 @@
package com.placeholder.ringhealth.core.designsystem.component
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
/**
* 统一卡片容器:白底、大圆角、轻阴影。所有页面内容块都应包在它里面。
*
* 颜色取自主题的 surface(由 [com.placeholder.ringhealth.core.designsystem.RingHealthTheme]
* 映射为设计系统的 CardSurface),因此升级深色模式时无需改动本文件。
*
* @param modifier 外部修饰符,会在 fillMaxWidth 之前生效。
* @param onClick 非空时整卡可点击(如水/体重等可跳转的卡片);为空则纯展示。
* @param content 卡片内容,作用域为 ColumnScope,默认内边距 16dp。
*/
@Composable
fun HealthCard(
modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null,
content: @Composable ColumnScope.() -> Unit,
) {
// 仅当传入点击回调时才挂 clickable,避免给纯展示卡片带来无谓的水波纹与无障碍焦点。
val clickModifier = if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier
Card(
modifier = modifier.fillMaxWidth().then(clickModifier),
shape = RoundedCornerShape(20.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp),
) {
Column(modifier = Modifier.padding(16.dp), content = content)
}
}
@@ -0,0 +1,61 @@
package com.placeholder.ringhealth.core.designsystem.component
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
/**
* 极简柱状图(Compose Canvas 手绘,无第三方依赖)。
* values 为空时不绘制;被高亮的一根用纯色,其余半透明。
*
* 几何说明:画布横向按 values.size 等分得到每个槽位 slot,柱宽取槽位的 65%,
* 余下 35% 作为间隔,柱子因此在槽内水平居中;柱高 = 画布高 × (value / maxValue),
* 底边贴合画布底边(y = 画布高 - 柱高),圆角半径取柱宽一半,形成胶囊柱。
* maxValue 下限为 1f,避免「全为 0」时出现 0 除。
*
* @param values 各柱数值(须为非负),按时间顺序从左到右。
* @param color 柱子颜色;高亮柱用纯色,其余用其 30% 透明度。
* @param modifier 外部修饰符。
* @param barHeight 画布高度,即最高柱的高度。
* @param highlightLast 为 true 时高亮最后一根(如「今天」),否则高亮数值最大的一根。
*/
@Composable
fun MetricBarChart(
values: List<Float>,
color: Color,
modifier: Modifier = Modifier,
barHeight: Dp = 120.dp,
highlightLast: Boolean = true,
) {
Canvas(modifier = modifier.fillMaxWidth().height(barHeight)) {
if (values.isEmpty()) return@Canvas
val maxValue = values.max().coerceAtLeast(1f)
val gapRatio = 0.35f
val slot = size.width / values.size
val barWidth = slot * (1f - gapRatio)
// 注意:maxValue 已被 coerceAtLeast(1f) 抬高,当所有值都小于 1 时
// values.indexOf(maxValue) 会返回 -1 导致没有任何柱子被高亮,
// 因此这里改为直接取最大值的下标。
val highlightIndex = if (highlightLast) values.lastIndex else values.indices.maxByOrNull { values[it] } ?: 0
values.forEachIndexed { index, value ->
val ratio = (value / maxValue).coerceIn(0f, 1f)
val h = size.height * ratio
val isHighlight = index == highlightIndex
drawRoundRect(
color = if (isHighlight) color else color.copy(alpha = 0.3f),
topLeft = Offset(x = index * slot + (slot - barWidth) / 2f, y = size.height - h),
size = Size(width = barWidth, height = h),
cornerRadius = CornerRadius(barWidth / 2f, barWidth / 2f),
)
}
}
}
@@ -0,0 +1,69 @@
package com.placeholder.ringhealth.core.designsystem.component
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.placeholder.ringhealth.core.designsystem.MetricTileValueStyle
import com.placeholder.ringhealth.core.designsystem.TextSecondary
/**
* 单个健康指标小卡:图标 + 标题 + 数值 + 单位。
*
* 文案全部由调用方传入(标题/数值/单位),组件内不出现任何用户可见字面量。
* 图标为纯装饰(contentDescription = null),语义由 title 承担,避免无障碍重复朗读。
*
* @param icon 指标图标,如 Icons.Filled.DirectionsWalk。
* @param iconTint 图标着色,通常取设计系统中该指标的语义色。
* @param title 指标名(由调用方从字符串资源取得)。
* @param value 主数值文本。
* @param unit 单位文本,如「步」「次/分」。
* @param modifier 外部修饰符。
* @param onClick 点击回调,默认空实现(配合 HealthCard 的整卡可点击)。
*/
@Composable
fun MetricTile(
icon: ImageVector,
iconTint: Color,
title: String,
value: String,
unit: String,
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
) {
HealthCard(modifier = modifier, onClick = onClick) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = icon, contentDescription = null, tint = iconTint, modifier = Modifier.size(18.dp))
Text(
text = title,
style = MaterialTheme.typography.bodyMedium,
color = TextSecondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(start = 6.dp),
)
}
Column(modifier = Modifier.padding(top = 6.dp), verticalArrangement = Arrangement.Bottom) {
Row(verticalAlignment = Alignment.Bottom) {
Text(text = value, style = MetricTileValueStyle)
Text(
text = unit,
style = MaterialTheme.typography.bodySmall,
color = TextSecondary,
modifier = Modifier.padding(start = 4.dp, bottom = 3.dp),
)
}
}
}
}
@@ -0,0 +1,70 @@
package com.placeholder.ringhealth.core.designsystem.component
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
/**
* 环形进度(首页大环与详情页复用)。progress 取 0f..1f。
* content 渲染在环中心(如步数大数字与单位)。
*
* 几何说明:描边以路径为中心向两侧各扩 stroke/2,因此圆弧的包围盒必须整体内缩
* stroke/2 后作为 topLeft,并以「边长 - stroke」作为 Size,否则圆环会被画布裁掉半个线宽。
* 进度从 12 点方向(-90°)开始顺时针增长;底环从 3 点方向起画满 360°(起点不影响整圆观感)。
*
* @param progress 进度比例,超出 0f..1f 会被裁剪。
* @param color 进度弧颜色,通常取设计系统的指标色。
* @param trackColor 底环颜色,通常取 DividerColor。
* @param modifier 外部修饰符。
* @param ringSize 圆环外径(含描边)。
* @param strokeWidth 描边宽度。
* @param content 环心内容。
*/
@Composable
fun RingProgress(
progress: Float,
color: Color,
trackColor: Color,
modifier: Modifier = Modifier,
ringSize: Dp = 180.dp,
strokeWidth: Dp = 14.dp,
content: @Composable () -> Unit = {},
) {
Box(modifier = modifier.size(ringSize), contentAlignment = Alignment.Center) {
Canvas(modifier = Modifier.size(ringSize)) {
val stroke = strokeWidth.toPx()
val inset = stroke / 2f
// 内缩后的包围盒:宽高均为画布尺寸减去一个线宽。
val arcSize = Size(this.size.width - stroke, this.size.height - stroke)
drawArc(
color = trackColor,
startAngle = 0f,
sweepAngle = 360f,
useCenter = false,
topLeft = Offset(inset, inset),
size = arcSize,
style = Stroke(width = stroke, cap = StrokeCap.Round),
)
drawArc(
color = color,
startAngle = -90f,
sweepAngle = 360f * progress.coerceIn(0f, 1f),
useCenter = false,
topLeft = Offset(inset, inset),
size = arcSize,
style = Stroke(width = stroke, cap = StrokeCap.Round),
)
}
content()
}
}
@@ -0,0 +1,37 @@
package com.placeholder.ringhealth.core.designsystem.component
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
/**
* 区块标题,可选右侧扩展内容(如「查看全部」按钮)。
*
* 标题与右侧内容左右两端对齐;trailing 为空时不占位。
*
* @param title 标题文本,由调用方从字符串资源取得。
* @param modifier 外部修饰符。
* @param trailing 右侧内容槽,例如一个 TextButton 或「更多」图标。
*/
@Composable
fun SectionHeader(
title: String,
modifier: Modifier = Modifier,
trailing: (@Composable () -> Unit)? = null,
) {
Row(
modifier = modifier.fillMaxWidth().padding(vertical = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(text = title, style = MaterialTheme.typography.titleMedium)
trailing?.invoke()
}
}
@@ -0,0 +1,51 @@
package com.placeholder.ringhealth.core.designsystem.component
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
/** 分段条的一段:权重决定宽度。 */
data class StatSegment(val weight: Float, val color: Color)
/**
* 横向分段条,用于睡眠分期(深睡/浅睡/清醒)等占比展示。
*
* 各段宽度按其 weight 在总权重中的占比分配;两端整体裁剪为半圆角。
* 权重被下限保护为极小正数,避免全零权重导致 Row 的 weight 分配异常。
*
* @param segments 分段列表,顺序即从左到右的绘制顺序。
* @param modifier 外部修饰符。
* @param barHeight 条高,同时决定两端圆角半径(barHeight / 2)。
*/
@Composable
fun StatBar(
segments: List<StatSegment>,
modifier: Modifier = Modifier,
barHeight: Dp = 12.dp,
) {
Row(
modifier = modifier
.fillMaxWidth()
.height(barHeight)
.clip(RoundedCornerShape(barHeight / 2)),
) {
segments.forEach { segment ->
Box(
modifier = Modifier
.weight(segment.weight.coerceAtLeast(0.0001f))
.fillMaxWidth()
.height(barHeight)
.background(segment.color),
)
}
}
}
@@ -0,0 +1,63 @@
package com.placeholder.ringhealth.domain.model
/** 健康指标类型。路由与详情页以此区分。 */
enum class MetricType {
STEP,
HEART_RATE,
SLEEP,
BLOOD_OXYGEN,
STRESS,
TEMPERATURE,
BLOOD_PRESSURE,
}
/** 历史数据的时间范围。 */
enum class DateRange { WEEK, MONTH }
/**
* 戒指设备状态。M2 接入蓝牙后由真实 SDK 填充,当前由 Mock 提供。
*/
data class RingDeviceState(
val connected: Boolean,
val name: String,
val batteryPercent: Int,
val firmwareVersion: String,
val charging: Boolean,
)
/**
* 今日健康概览。字段为 null 表示该指标未开启或暂无数据,UI 需展示“未开启”占位而非 0。
*/
data class HealthSummary(
val steps: Int,
val stepGoal: Int,
val calories: Int,
val distanceKm: Double,
val heartRate: Int?,
val sleepMinutes: Int?,
val bloodOxygen: Int?,
val stress: Int?,
val temperature: Double?,
val bloodPressure: String?,
)
/** 一条历史数据点。 */
data class MetricPoint(val timestamp: Long, val value: Double)
/** 一条运动记录。 */
data class WorkoutRecord(
val id: String,
val type: String,
val startTime: Long,
val durationMinutes: Int,
val distanceKm: Double,
val calories: Int,
)
/** 戒指功能开关项(对应协议中的自动监测开关)。 */
data class DeviceFunctionSetting(
val id: String,
val title: String,
val subtitle: String,
val enabled: Boolean,
)
@@ -0,0 +1,42 @@
package com.placeholder.ringhealth.domain.repository
import com.placeholder.ringhealth.domain.model.DateRange
import com.placeholder.ringhealth.domain.model.DeviceFunctionSetting
import com.placeholder.ringhealth.domain.model.HealthSummary
import com.placeholder.ringhealth.domain.model.MetricPoint
import com.placeholder.ringhealth.domain.model.MetricType
import com.placeholder.ringhealth.domain.model.RingDeviceState
import com.placeholder.ringhealth.domain.model.WorkoutRecord
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
/**
* 健康数据来源。M1 由 Mock 实现,M2/M3 由厂商 SDK 适配层实现,UI 无需改动。
*/
interface HealthDataRepository {
/** 今日概览,随数据变化持续发射。 */
fun observeTodaySummary(): Flow<HealthSummary>
/** 指定指标在指定时间范围内的历史数据。 */
fun observeMetricHistory(metric: MetricType, range: DateRange): Flow<List<MetricPoint>>
/** 运动记录列表。 */
fun observeWorkouts(): Flow<List<WorkoutRecord>>
}
/**
* 戒指设备状态与功能开关。M2 接入蓝牙后由真实实现替换。
*/
interface RingDeviceRepository {
/** 设备状态(连接/电量/固件)。 */
val deviceState: StateFlow<RingDeviceState>
/** 功能开关列表。 */
val functionSettings: StateFlow<List<DeviceFunctionSetting>>
/** 主动刷新设备状态。 */
suspend fun refreshDeviceState()
/** 切换某个功能开关(M1 仅改内存状态)。 */
fun setFunctionEnabled(id: String, enabled: Boolean)
}
+5
View File
@@ -1,4 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">RingHealth</string> <string name="app_name">RingHealth</string>
<string name="stress_relaxed">放松</string>
<string name="stress_normal">正常</string>
<string name="stress_medium">中等</string>
<string name="stress_high">偏高</string>
</resources> </resources>
@@ -0,0 +1,49 @@
package com.placeholder.ringhealth.core.common
import com.placeholder.ringhealth.R
import org.junit.Assert.assertEquals
import org.junit.Test
class FormattersTest {
@Test
fun `formatSleepDuration 处理小时与分钟`() {
assertEquals("7小时32分", formatSleepDuration(452))
assertEquals("45分", formatSleepDuration(45))
assertEquals("8小时", formatSleepDuration(480))
assertEquals("0分", formatSleepDuration(0))
}
@Test
fun `formatDuration 处理小时与分钟`() {
assertEquals("1小时05分", formatDuration(65))
assertEquals("42分", formatDuration(42))
}
@Test
fun `formatSteps 添加千分位`() {
assertEquals("8,426", formatSteps(8426))
assertEquals("0", formatSteps(0))
assertEquals("1,234,567", formatSteps(1234567))
}
@Test
fun `formatDistance 保留两位小数`() {
assertEquals("6.21", formatDistance(6.212))
assertEquals("0.00", formatDistance(0.0))
}
@Test
fun `formatCalories 只返回千分位数字`() {
assertEquals("327", formatCalories(327))
assertEquals("1,234", formatCalories(1234))
}
@Test
fun `stressLevelLabelRes 按区间映射到对应字符串资源`() {
assertEquals(R.string.stress_relaxed, stressLevelLabelRes(20))
assertEquals(R.string.stress_normal, stressLevelLabelRes(45))
assertEquals(R.string.stress_medium, stressLevelLabelRes(65))
assertEquals(R.string.stress_high, stressLevelLabelRes(90))
}
}