feat: Experimenting with desktop UI and manual implementation of i18n
Plan to migrate to an i18n library Plan to finalize UI design before continuing development Next step: Implement data retrieval for all platforms
This commit is contained in:
parent
fdd413d7ff
commit
1c61774c04
@ -16,12 +16,12 @@ kotlin {
|
||||
jvmTarget.set(JvmTarget.JVM_11)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
jvm("desktop")
|
||||
|
||||
|
||||
sourceSets {
|
||||
val desktopMain by getting
|
||||
|
||||
|
||||
androidMain.dependencies {
|
||||
implementation(compose.preview)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
@ -29,13 +29,14 @@ kotlin {
|
||||
commonMain.dependencies {
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material)
|
||||
implementation(compose.material3)
|
||||
implementation(compose.ui)
|
||||
implementation(compose.components.resources)
|
||||
implementation(compose.components.uiToolingPreview)
|
||||
implementation(libs.androidx.lifecycle.viewmodel)
|
||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||
implementation(projects.shared)
|
||||
implementation(compose.materialIconsExtended)
|
||||
}
|
||||
desktopMain.dependencies {
|
||||
implementation(compose.desktop.currentOs)
|
||||
|
||||
@ -4,9 +4,7 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
32
composeApp/src/commonMain/resources/MR/base/strings.xml
Normal file
32
composeApp/src/commonMain/resources/MR/base/strings.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Activity Analyzer</string>
|
||||
<string name="home">Home</string>
|
||||
<string name="analytics">Analytics</string>
|
||||
<string name="reports">Reports</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="about">About</string>
|
||||
<string name="quick_actions">Quick Actions</string>
|
||||
<string name="new_analysis">New Analysis</string>
|
||||
<string name="import_data">Import Data</string>
|
||||
<string name="recent_activities">Recent Activities</string>
|
||||
<string name="analysis_report">Analysis Report</string>
|
||||
<string name="data_import">Data Import</string>
|
||||
<string name="analysis_tools">Analysis Tools</string>
|
||||
<string name="time_analysis">Time Analysis</string>
|
||||
<string name="distribution_analysis">Distribution Analysis</string>
|
||||
<string name="my_reports">My Reports</string>
|
||||
<string name="monthly_report">Monthly Activity Report</string>
|
||||
<string name="weekly_report">Weekly Activity Report</string>
|
||||
<string name="download">Download</string>
|
||||
<string name="data_storage">Data Storage Location</string>
|
||||
<string name="default_location">Default Location</string>
|
||||
<string name="change">Change</string>
|
||||
<string name="auto_backup">Auto Backup</string>
|
||||
<string name="daily">Daily</string>
|
||||
<string name="notifications">Notifications</string>
|
||||
<string name="enabled">Enabled</string>
|
||||
<string name="version">Version</string>
|
||||
<string name="app_description">A powerful activity analysis tool to help you better understand and optimize your activity data.</string>
|
||||
<string name="check_updates">Check Updates</string>
|
||||
</resources>
|
||||
32
composeApp/src/commonMain/resources/MR/zh/strings.xml
Normal file
32
composeApp/src/commonMain/resources/MR/zh/strings.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">活动分析器</string>
|
||||
<string name="home">首页</string>
|
||||
<string name="analytics">数据分析</string>
|
||||
<string name="reports">报告</string>
|
||||
<string name="settings">设置</string>
|
||||
<string name="about">关于</string>
|
||||
<string name="quick_actions">快速操作</string>
|
||||
<string name="new_analysis">新建分析</string>
|
||||
<string name="import_data">导入数据</string>
|
||||
<string name="recent_activities">最近活动</string>
|
||||
<string name="analysis_report">数据分析报告</string>
|
||||
<string name="data_import">活动数据导入</string>
|
||||
<string name="analysis_tools">分析工具</string>
|
||||
<string name="time_analysis">时间分析</string>
|
||||
<string name="distribution_analysis">分布分析</string>
|
||||
<string name="my_reports">我的报告</string>
|
||||
<string name="monthly_report">月度活动报告</string>
|
||||
<string name="weekly_report">周活动分析</string>
|
||||
<string name="download">下载</string>
|
||||
<string name="data_storage">数据存储位置</string>
|
||||
<string name="default_location">默认位置</string>
|
||||
<string name="change">更改</string>
|
||||
<string name="auto_backup">自动备份</string>
|
||||
<string name="daily">每天</string>
|
||||
<string name="notifications">通知设置</string>
|
||||
<string name="enabled">开启</string>
|
||||
<string name="version">版本</string>
|
||||
<string name="app_description">一个强大的活动分析工具,帮助您更好地理解和优化您的活动数据。</string>
|
||||
<string name="check_updates">检查更新</string>
|
||||
</resources>
|
||||
@ -0,0 +1,612 @@
|
||||
package com.grtsinry43.activityanalyzer
|
||||
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.TextButton
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun DesktopApp() {
|
||||
var selectedItem by remember { mutableStateOf(0) }
|
||||
var isDarkTheme by remember { mutableStateOf(false) }
|
||||
|
||||
val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFF5F5F5)
|
||||
val surfaceColor = if (isDarkTheme) Color(0xFF2D2D2D) else Color.White
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
val accentColor = Color(0xFF007AFF)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(backgroundColor)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
// Sidebar
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(200.dp)
|
||||
.fillMaxHeight()
|
||||
.background(surfaceColor)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
"Activity Analyzer",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = textColor
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
// Navigation items
|
||||
NavItem(
|
||||
icon = Icons.Default.Home,
|
||||
text = "Home",
|
||||
isSelected = selectedItem == 0,
|
||||
onClick = { selectedItem = 0 },
|
||||
textColor = textColor,
|
||||
accentColor = accentColor
|
||||
)
|
||||
NavItem(
|
||||
icon = Icons.Default.BarChart,
|
||||
text = "Analytics",
|
||||
isSelected = selectedItem == 1,
|
||||
onClick = { selectedItem = 1 },
|
||||
textColor = textColor,
|
||||
accentColor = accentColor
|
||||
)
|
||||
NavItem(
|
||||
icon = Icons.Default.Dataset,
|
||||
text = "Reports",
|
||||
isSelected = selectedItem == 2,
|
||||
onClick = { selectedItem = 2 },
|
||||
textColor = textColor,
|
||||
accentColor = accentColor
|
||||
)
|
||||
NavItem(
|
||||
icon = Icons.Default.Settings,
|
||||
text = "Settings",
|
||||
isSelected = selectedItem == 3,
|
||||
onClick = { selectedItem = 3 },
|
||||
textColor = textColor,
|
||||
accentColor = accentColor
|
||||
)
|
||||
|
||||
// Spacer for alignment
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
// Theme switch
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(if (isDarkTheme) Color(0xFF3D3D3D) else Color(0xFFE8E8E8))
|
||||
.padding(8.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
"Dark Theme",
|
||||
color = textColor
|
||||
)
|
||||
Switch(
|
||||
checked = isDarkTheme,
|
||||
onCheckedChange = { isDarkTheme = it },
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = accentColor,
|
||||
checkedTrackColor = accentColor.copy(alpha = 0.5f)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main content
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(24.dp)
|
||||
) {
|
||||
when (selectedItem) {
|
||||
0 -> HomeScreen(textColor = textColor, surfaceColor = surfaceColor)
|
||||
1 -> AnalyticsScreen(textColor = textColor, surfaceColor = surfaceColor)
|
||||
2 -> ReportsScreen(textColor = textColor, surfaceColor = surfaceColor)
|
||||
3 -> SettingsScreen(textColor = textColor, surfaceColor = surfaceColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavItem(
|
||||
icon: ImageVector,
|
||||
text: String,
|
||||
isSelected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
textColor: Color,
|
||||
accentColor: Color
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(if (isSelected) accentColor.copy(alpha = 0.1f) else Color.Transparent)
|
||||
.padding(12.dp)
|
||||
.clickable(
|
||||
onClick = onClick,
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = if (isSelected) accentColor else textColor
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
color = if (isSelected) accentColor else textColor,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HomeScreen(textColor: Color, surfaceColor: Color) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Welcome Back!",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
|
||||
// Quick actions
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.height(150.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Quick Actions",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
ActionButton(
|
||||
icon = Icons.Default.Add,
|
||||
text = "New Activity",
|
||||
onClick = { /* TODO */ }
|
||||
)
|
||||
ActionButton(
|
||||
icon = Icons.Default.Upload,
|
||||
text = "Import Data",
|
||||
onClick = { /* TODO */ }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recent activities
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp)),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Recent Activities",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
ActivityItem(
|
||||
title = "Activity Report Generated",
|
||||
time = "2024-04-12 14:30",
|
||||
icon = Icons.Default.Description
|
||||
)
|
||||
ActivityItem(
|
||||
title = "Data Imported",
|
||||
time = "2024-04-12 10:15",
|
||||
icon = Icons.Default.Upload
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ActionButton(
|
||||
icon: ImageVector,
|
||||
text: String,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(Color(0xFF007AFF).copy(alpha = 0.1f))
|
||||
.clickable(onClick = onClick)
|
||||
.padding(16.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF007AFF)
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
color = Color(0xFF007AFF),
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ActivityItem(
|
||||
title: String,
|
||||
time: String,
|
||||
icon: ImageVector
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF007AFF)
|
||||
)
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
fontSize = 16.sp,
|
||||
color = Color.Black
|
||||
)
|
||||
Text(
|
||||
text = time,
|
||||
fontSize = 14.sp,
|
||||
color = Color.Gray
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AnalyticsScreen(textColor: Color, surfaceColor: Color) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
Text(
|
||||
"Analytics",
|
||||
fontSize = 32.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = textColor
|
||||
)
|
||||
|
||||
// Analysis tools card
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(surfaceColor)
|
||||
.padding(24.dp)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
"Analysis Tools",
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = textColor
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
AnalysisToolButton(
|
||||
icon = Icons.Default.Info,
|
||||
text = "Time Analysis",
|
||||
onClick = {/* TODO */ }
|
||||
)
|
||||
AnalysisToolButton(
|
||||
icon = Icons.Default.Info,
|
||||
text = "Distribution Analysis",
|
||||
onClick = {/* TODO */ }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AnalysisToolButton(
|
||||
icon: ImageVector,
|
||||
text: String,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(Color(0xFF007AFF).copy(alpha = 0.1f))
|
||||
.clickable(onClick = onClick)
|
||||
.padding(16.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF007AFF)
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
color = Color(0xFF007AFF),
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ReportsScreen(textColor: Color, surfaceColor: Color) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
Text(
|
||||
"Reports",
|
||||
fontSize = 32.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = textColor
|
||||
)
|
||||
|
||||
// Report list
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(surfaceColor)
|
||||
.padding(24.dp)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
"My Reports",
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = textColor
|
||||
)
|
||||
ReportItem(
|
||||
title = "Monthly Activity Report",
|
||||
period = "March 2024",
|
||||
icon = Icons.Default.Info
|
||||
)
|
||||
ReportItem(
|
||||
title = "Weekly Activity Analysis",
|
||||
period = "Week 12, 2024",
|
||||
icon = Icons.Default.Info
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ReportItem(
|
||||
title: String,
|
||||
period: String,
|
||||
icon: ImageVector
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF007AFF)
|
||||
)
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
fontSize = 16.sp,
|
||||
color = Color.Black
|
||||
)
|
||||
Text(
|
||||
text = period,
|
||||
fontSize = 14.sp,
|
||||
color = Color.Gray
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = { /* TODO */ }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Info,
|
||||
contentDescription = "Download",
|
||||
tint = Color(0xFF007AFF)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(textColor: Color, surfaceColor: Color) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
Text(
|
||||
"Settings",
|
||||
fontSize = 32.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = textColor
|
||||
)
|
||||
|
||||
// Settings options
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(surfaceColor)
|
||||
.padding(24.dp)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
SettingItem(
|
||||
title = "Data Storage Location",
|
||||
subtitle = "Default Location",
|
||||
icon = Icons.Default.Info,
|
||||
onClick = {/* TODO */ }
|
||||
)
|
||||
SettingItem(
|
||||
title = "Auto Backup",
|
||||
subtitle = "Daily",
|
||||
icon = Icons.Default.Info,
|
||||
showSwitch = true
|
||||
)
|
||||
SettingItem(
|
||||
title = "Notification Settings",
|
||||
subtitle = "Enabled",
|
||||
icon = Icons.Default.Info,
|
||||
showSwitch = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingItem(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
icon: ImageVector,
|
||||
onClick: (() -> Unit)? = null,
|
||||
showSwitch: Boolean = false
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF007AFF)
|
||||
)
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
fontSize = 16.sp,
|
||||
color = Color.Black
|
||||
)
|
||||
Text(
|
||||
text = subtitle,
|
||||
fontSize = 14.sp,
|
||||
color = Color.Gray
|
||||
)
|
||||
}
|
||||
}
|
||||
if (showSwitch) {
|
||||
Switch(
|
||||
checked = true,
|
||||
onCheckedChange = {/* TODO */ },
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = Color(0xFF007AFF),
|
||||
checkedTrackColor = Color(0xFF007AFF).copy(alpha = 0.5f)
|
||||
)
|
||||
)
|
||||
} else if (onClick != null) {
|
||||
TextButton(onClick = onClick) {
|
||||
Text(
|
||||
"Change",
|
||||
color = Color(0xFF007AFF)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,6 @@ fun main() = application {
|
||||
onCloseRequest = ::exitApplication,
|
||||
title = "Activity Analyzer",
|
||||
) {
|
||||
App()
|
||||
DesktopApp()
|
||||
}
|
||||
}
|
||||
7
iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@ -0,0 +1,24 @@
|
||||
package com.grtsinry43.activityanalyzer.i18n
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
|
||||
actual class StringResource(private val context: Context) {
|
||||
actual fun getString(key: String): String {
|
||||
val resourceId = context.resources.getIdentifier(key, "string", context.packageName)
|
||||
return if (resourceId != 0) {
|
||||
context.getString(resourceId)
|
||||
} else {
|
||||
key
|
||||
}
|
||||
}
|
||||
|
||||
actual fun getString(key: String, vararg args: Any): String {
|
||||
val resourceId = context.resources.getIdentifier(key, "string", context.packageName)
|
||||
return if (resourceId != 0) {
|
||||
context.getString(resourceId, *args)
|
||||
} else {
|
||||
key
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.grtsinry43.activityanalyzer.i18n
|
||||
|
||||
import android.content.Context
|
||||
|
||||
actual object StringResourceFactory {
|
||||
private var context: Context? = null
|
||||
|
||||
fun initialize(context: Context) {
|
||||
this.context = context.applicationContext
|
||||
}
|
||||
|
||||
actual fun create(): StringResource {
|
||||
return StringResource(context ?: throw IllegalStateException("Context not initialized"))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.grtsinry43.activityanalyzer.i18n
|
||||
|
||||
expect class StringResource {
|
||||
fun getString(key: String): String
|
||||
fun getString(key: String, vararg args: Any): String
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package com.grtsinry43.activityanalyzer.i18n
|
||||
|
||||
expect object StringResourceFactory {
|
||||
fun create(): StringResource
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package com.grtsinry43.activityanalyzer.i18n
|
||||
|
||||
import java.util.Properties
|
||||
import java.io.InputStream
|
||||
import java.text.MessageFormat
|
||||
|
||||
actual class StringResource {
|
||||
private val properties = Properties()
|
||||
private var currentLocale: String = "en"
|
||||
|
||||
init {
|
||||
loadProperties("en")
|
||||
}
|
||||
|
||||
fun setLocale(locale: String) {
|
||||
if (currentLocale != locale) {
|
||||
currentLocale = locale
|
||||
loadProperties(locale)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadProperties(locale: String) {
|
||||
properties.clear()
|
||||
val baseName = "strings"
|
||||
val resourceName = if (locale == "en") "$baseName.properties" else "${baseName}_$locale.properties"
|
||||
|
||||
val inputStream = javaClass.classLoader.getResourceAsStream(resourceName)
|
||||
if (inputStream != null) {
|
||||
properties.load(inputStream)
|
||||
inputStream.close()
|
||||
}
|
||||
}
|
||||
|
||||
actual fun getString(key: String): String {
|
||||
return properties.getProperty(key, key)
|
||||
}
|
||||
|
||||
actual fun getString(key: String, vararg args: Any): String {
|
||||
val pattern = properties.getProperty(key, key)
|
||||
return MessageFormat.format(pattern, *args)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.grtsinry43.activityanalyzer.i18n
|
||||
|
||||
actual object StringResourceFactory {
|
||||
private val stringResource = StringResource()
|
||||
|
||||
actual fun create(): StringResource {
|
||||
return stringResource
|
||||
}
|
||||
|
||||
fun setLocale(locale: String) {
|
||||
stringResource.setLocale(locale)
|
||||
}
|
||||
}
|
||||
22
shared/src/desktopMain/resources/strings.properties
Normal file
22
shared/src/desktopMain/resources/strings.properties
Normal file
@ -0,0 +1,22 @@
|
||||
app.title=Activity Analyzer
|
||||
home.welcome=Welcome to Activity Analyzer
|
||||
home.quickActions=Quick Actions
|
||||
home.newAnalysis=New Analysis
|
||||
home.importData=Import Data
|
||||
home.recentActivities=Recent Activities
|
||||
analytics.title=Data Analysis
|
||||
analytics.tools=Analysis Tools
|
||||
analytics.timeAnalysis=Time Analysis
|
||||
analytics.distributionAnalysis=Distribution Analysis
|
||||
reports.title=Reports
|
||||
reports.myReports=My Reports
|
||||
reports.monthlyReport=Monthly Activity Report
|
||||
reports.weeklyReport=Weekly Activity Report
|
||||
settings.title=Settings
|
||||
settings.dataStorage=Data Storage Location
|
||||
settings.autoBackup=Auto Backup
|
||||
settings.notifications=Notifications
|
||||
about.title=About Activity Analyzer
|
||||
about.version=Version {0}
|
||||
about.description=A powerful activity analysis tool to help you better understand and optimize your activity data.
|
||||
about.checkUpdate=Check for Updates
|
||||
22
shared/src/desktopMain/resources/strings_zh.properties
Normal file
22
shared/src/desktopMain/resources/strings_zh.properties
Normal file
@ -0,0 +1,22 @@
|
||||
app.title=活动分析器
|
||||
home.welcome=欢迎使用活动分析器
|
||||
home.quickActions=快速操作
|
||||
home.newAnalysis=新建分析
|
||||
home.importData=导入数据
|
||||
home.recentActivities=最近活动
|
||||
analytics.title=数据分析
|
||||
analytics.tools=分析工具
|
||||
analytics.timeAnalysis=时间分析
|
||||
analytics.distributionAnalysis=分布分析
|
||||
reports.title=报告
|
||||
reports.myReports=我的报告
|
||||
reports.monthlyReport=月度活动报告
|
||||
reports.weeklyReport=周活动分析
|
||||
settings.title=设置
|
||||
settings.dataStorage=数据存储位置
|
||||
settings.autoBackup=自动备份
|
||||
settings.notifications=通知设置
|
||||
about.title=关于活动分析器
|
||||
about.version=版本 {0}
|
||||
about.description=一个强大的活动分析工具,帮助您更好地理解和优化您的活动数据。
|
||||
about.checkUpdate=检查更新
|
||||
@ -0,0 +1,29 @@
|
||||
package com.grtsinry43.activityanalyzer.i18n
|
||||
|
||||
import platform.Foundation.NSBundle
|
||||
import platform.Foundation.NSString
|
||||
import platform.Foundation.stringWithFormat
|
||||
|
||||
actual class StringResource {
|
||||
private val bundle = NSBundle.mainBundle
|
||||
|
||||
actual fun getString(key: String): String {
|
||||
return bundle.localizedStringForKey(key, "", null) ?: key
|
||||
}
|
||||
|
||||
actual fun getString(key: String, vararg args: Any): String {
|
||||
val format = bundle.localizedStringForKey(key, "", null) ?: key
|
||||
return when (args.size) {
|
||||
0 -> format
|
||||
1 -> NSString.stringWithFormat(format, args[0])
|
||||
2 -> NSString.stringWithFormat(format, args[0], args[1])
|
||||
3 -> NSString.stringWithFormat(format, args[0], args[1], args[2])
|
||||
4 -> NSString.stringWithFormat(format, args[0], args[1], args[2], args[3])
|
||||
else -> {
|
||||
// 处理更多参数情况
|
||||
// 简单方案:只使用前4个参数
|
||||
NSString.stringWithFormat(format, args[0], args[1], args[2], args[3])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package com.grtsinry43.activityanalyzer.i18n
|
||||
|
||||
actual object StringResourceFactory {
|
||||
actual fun create(): StringResource {
|
||||
return StringResource()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package com.grtsinry43.activityanalyzer.i18n
|
||||
|
||||
import java.util.Properties
|
||||
import java.io.InputStream
|
||||
import java.text.MessageFormat
|
||||
|
||||
actual class StringResource {
|
||||
private val properties = Properties()
|
||||
private var currentLocale: String = "en"
|
||||
|
||||
init {
|
||||
loadProperties("en")
|
||||
}
|
||||
|
||||
fun setLocale(locale: String) {
|
||||
if (currentLocale != locale) {
|
||||
currentLocale = locale
|
||||
loadProperties(locale)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadProperties(locale: String) {
|
||||
properties.clear()
|
||||
val baseName = "strings"
|
||||
val resourceName = if (locale == "en") "$baseName.properties" else "${baseName}_$locale.properties"
|
||||
|
||||
val inputStream = javaClass.classLoader.getResourceAsStream(resourceName)
|
||||
if (inputStream != null) {
|
||||
properties.load(inputStream)
|
||||
inputStream.close()
|
||||
}
|
||||
}
|
||||
|
||||
actual fun getString(key: String): String {
|
||||
return properties.getProperty(key, key)
|
||||
}
|
||||
|
||||
actual fun getString(key: String, vararg args: Any): String {
|
||||
val pattern = properties.getProperty(key, key)
|
||||
return MessageFormat.format(pattern, *args)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.grtsinry43.activityanalyzer.i18n
|
||||
|
||||
actual object StringResourceFactory {
|
||||
private val stringResource = StringResource()
|
||||
|
||||
actual fun create(): StringResource {
|
||||
return stringResource
|
||||
}
|
||||
|
||||
fun setLocale(locale: String) {
|
||||
stringResource.setLocale(locale)
|
||||
}
|
||||
}
|
||||
22
shared/src/jvmMain/resources/strings.properties
Normal file
22
shared/src/jvmMain/resources/strings.properties
Normal file
@ -0,0 +1,22 @@
|
||||
app.title=Activity Analyzer
|
||||
home.welcome=Welcome to Activity Analyzer
|
||||
home.quickActions=Quick Actions
|
||||
home.newAnalysis=New Analysis
|
||||
home.importData=Import Data
|
||||
home.recentActivities=Recent Activities
|
||||
analytics.title=Data Analysis
|
||||
analytics.tools=Analysis Tools
|
||||
analytics.timeAnalysis=Time Analysis
|
||||
analytics.distributionAnalysis=Distribution Analysis
|
||||
reports.title=Reports
|
||||
reports.myReports=My Reports
|
||||
reports.monthlyReport=Monthly Activity Report
|
||||
reports.weeklyReport=Weekly Activity Report
|
||||
settings.title=Settings
|
||||
settings.dataStorage=Data Storage Location
|
||||
settings.autoBackup=Auto Backup
|
||||
settings.notifications=Notifications
|
||||
about.title=About Activity Analyzer
|
||||
about.version=Version {0}
|
||||
about.description=A powerful activity analysis tool to help you better understand and optimize your activity data.
|
||||
about.checkUpdate=Check for Updates
|
||||
22
shared/src/jvmMain/resources/strings_zh.properties
Normal file
22
shared/src/jvmMain/resources/strings_zh.properties
Normal file
@ -0,0 +1,22 @@
|
||||
app.title=活动分析器
|
||||
home.welcome=欢迎使用活动分析器
|
||||
home.quickActions=快速操作
|
||||
home.newAnalysis=新建分析
|
||||
home.importData=导入数据
|
||||
home.recentActivities=最近活动
|
||||
analytics.title=数据分析
|
||||
analytics.tools=分析工具
|
||||
analytics.timeAnalysis=时间分析
|
||||
analytics.distributionAnalysis=分布分析
|
||||
reports.title=报告
|
||||
reports.myReports=我的报告
|
||||
reports.monthlyReport=月度活动报告
|
||||
reports.weeklyReport=周活动分析
|
||||
settings.title=设置
|
||||
settings.dataStorage=数据存储位置
|
||||
settings.autoBackup=自动备份
|
||||
settings.notifications=通知设置
|
||||
about.title=关于活动分析器
|
||||
about.version=版本 {0}
|
||||
about.description=一个强大的活动分析工具,帮助您更好地理解和优化您的活动数据。
|
||||
about.checkUpdate=检查更新
|
||||
Loading…
x
Reference in New Issue
Block a user