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)
|
jvmTarget.set(JvmTarget.JVM_11)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jvm("desktop")
|
jvm("desktop")
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
val desktopMain by getting
|
val desktopMain by getting
|
||||||
|
|
||||||
androidMain.dependencies {
|
androidMain.dependencies {
|
||||||
implementation(compose.preview)
|
implementation(compose.preview)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
@ -29,13 +29,14 @@ kotlin {
|
|||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
implementation(compose.runtime)
|
implementation(compose.runtime)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
implementation(compose.material)
|
implementation(compose.material3)
|
||||||
implementation(compose.ui)
|
implementation(compose.ui)
|
||||||
implementation(compose.components.resources)
|
implementation(compose.components.resources)
|
||||||
implementation(compose.components.uiToolingPreview)
|
implementation(compose.components.uiToolingPreview)
|
||||||
implementation(libs.androidx.lifecycle.viewmodel)
|
implementation(libs.androidx.lifecycle.viewmodel)
|
||||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||||
implementation(projects.shared)
|
implementation(projects.shared)
|
||||||
|
implementation(compose.materialIconsExtended)
|
||||||
}
|
}
|
||||||
desktopMain.dependencies {
|
desktopMain.dependencies {
|
||||||
implementation(compose.desktop.currentOs)
|
implementation(compose.desktop.currentOs)
|
||||||
|
|||||||
@ -4,9 +4,7 @@ import androidx.compose.animation.AnimatedVisibility
|
|||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.material.Button
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.material.MaterialTheme
|
|
||||||
import androidx.compose.material.Text
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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,
|
onCloseRequest = ::exitApplication,
|
||||||
title = "Activity Analyzer",
|
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