diff --git a/README.md b/README.md
index 619e590..a03fd61 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
-# Activity Analyzer
+# Chronosight

[](https://kotlinlang.org)
**English | [简体中文](README_zh.md)**
-**Understand your digital habits with Activity Analyzer, a cross-platform screen time tracking and analysis application.**
+**Understand your digital habits with Chronosight, a cross-platform screen time tracking and analysis application.**
-Gain valuable insights into how you spend your time on your devices. Activity Analyzer helps you track your screen time, analyze your app usage, and ultimately promotes better digital well-being across your phone, tablet, and computer.
+Gain valuable insights into how you spend your time on your devices. Chronosight helps you track your screen time, analyze your app usage, and ultimately promotes better digital well-being across your phone, tablet, and computer.
## Key Features
@@ -19,7 +19,7 @@ Gain valuable insights into how you spend your time on your devices. Activity An
## Technology Stack
-Activity Analyzer is built with the following technologies:
+Chronosight is built with the following technologies:
* **Kotlin Multiplatform Mobile (KMM):** Powers the core business logic, data analysis, and shared functionality across all platforms.
* **Android:** Native UI development using Kotlin and Jetpack Compose.
@@ -29,7 +29,7 @@ Activity Analyzer is built with the following technologies:
## Platform Support
-Activity Analyzer is currently targeting the following platforms:
+Chronosight is currently targeting the following platforms:
* 📱 **Mobile:** Android, iOS
* 💻 **Desktop:** macOS, Windows, Linux
@@ -39,7 +39,7 @@ Activity Analyzer is currently targeting the following platforms:
If you're interested in contributing or building the project yourself, here's a quick guide:
1. **Prerequisites:** Ensure you have the necessary SDKs and development environments set up for Android, iOS, and Desktop Kotlin development.
-2. **Clone the Repository:** `git clone https://github.com/grtsinry43/ActivityAnalyzer.git`
+2. **Clone the Repository:** `git clone https://github.com/grtsinry43/Chronosight.git`
3. **Open project:** Open the project in IntelliJ IDEA (or Android Studio).
4. **Build and Run:**
* **Android:** Run the `androidApp` module.
diff --git a/README_zh.md b/README_zh.md
index f32a98e..f41bf66 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -1,13 +1,13 @@
-# Activity Analyzer
+# Chronosight

[](https://kotlinlang.org)
**[English](README.md) | 简体中文**
-**时间都去哪儿了?通过 Activity Analyzer 了解您的数字习惯,跨平台跟踪和分析您的屏幕时间。**
+**时间都去哪儿了?通过 Chronosight 了解您的数字习惯,跨平台跟踪和分析您的屏幕时间。**
-深入了解您在设备上花费时间的方式。Activity Analyzer 帮助您跟踪屏幕使用时间、分析您的应用使用情况,并最终促进您在手机、平板电脑和电脑上实现更好的数字健康。
+深入了解您在设备上花费时间的方式。Chronosight 帮助您跟踪屏幕使用时间、分析您的应用使用情况,并最终促进您在手机、平板电脑和电脑上实现更好的数字健康。
## 主要功能
@@ -35,7 +35,7 @@
如果您有兴趣贡献代码或自行构建项目,请参考以下简要指南:
1. **前提条件:** 确保您已设置好 Android、iOS 和桌面 Kotlin 开发所需的 SDK 和开发环境。
-2. **克隆代码仓库:** `git clone https://github.com/grtsinry43/ActivityAnalyzer.git`
+2. **克隆代码仓库:** `git clone https://github.com/grtsinry43/Chronosight.git`
3. **打开:** 在 IntelliJ IDEA 或 Android Studio 中打开项目。
4. **构建并运行:**
* **Android:** 运行 `androidApp` 模块。
diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts
index 04ef0cc..fce7116 100644
--- a/composeApp/build.gradle.kts
+++ b/composeApp/build.gradle.kts
@@ -37,6 +37,7 @@ kotlin {
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(projects.shared)
implementation(compose.materialIconsExtended)
+ implementation(libs.moko.resources.compose)
}
desktopMain.dependencies {
implementation(compose.desktop.currentOs)
@@ -46,11 +47,11 @@ kotlin {
}
android {
- namespace = "com.grtsinry43.activityanalyzer"
+ namespace = "com.grtsinry43.chronosight"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
- applicationId = "com.grtsinry43.activityanalyzer"
+ applicationId = "com.grtsinry43.chronosight"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 1
@@ -78,11 +79,11 @@ dependencies {
compose.desktop {
application {
- mainClass = "com.grtsinry43.activityanalyzer.MainKt"
+ mainClass = "com.grtsinry43.chronosight.MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
- packageName = "com.grtsinry43.activityanalyzer"
+ packageName = "com.grtsinry43.chronosight"
packageVersion = "1.0.0"
}
}
diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml
index d56ee80..6eb1870 100644
--- a/composeApp/src/androidMain/AndroidManifest.xml
+++ b/composeApp/src/androidMain/AndroidManifest.xml
@@ -1,12 +1,16 @@
-
+
+
+
-
\ No newline at end of file
+
+
diff --git a/composeApp/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/MainActivity.kt b/composeApp/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/MainActivity.kt
deleted file mode 100644
index 6926e4b..0000000
--- a/composeApp/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/MainActivity.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.grtsinry43.activityanalyzer
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.tooling.preview.Preview
-
-class MainActivity : ComponentActivity() {
- @OptIn(ExperimentalMaterial3Api::class)
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setContent {
- App()
- }
- }
-}
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Preview
-@Composable
-fun AppAndroidPreview() {
- App()
-}
\ No newline at end of file
diff --git a/composeApp/src/androidMain/kotlin/com/grtsinry43/chronosight/MainActivity.kt b/composeApp/src/androidMain/kotlin/com/grtsinry43/chronosight/MainActivity.kt
new file mode 100644
index 0000000..a2c3ea6
--- /dev/null
+++ b/composeApp/src/androidMain/kotlin/com/grtsinry43/chronosight/MainActivity.kt
@@ -0,0 +1,45 @@
+package com.grtsinry43.chronosight
+
+import android.content.Intent
+import android.os.Bundle
+import android.provider.Settings
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.tooling.preview.Preview
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun AppWithUsageAccessHandler() {
+ val context = LocalContext.current
+ App(
+ onRequestUsageAccess = {
+ try {
+ val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ context.startActivity(intent)
+ } catch (_: Exception) {}
+ }
+ )
+}
+
+class MainActivity : ComponentActivity() {
+ @OptIn(ExperimentalMaterial3Api::class)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ AppWithUsageAccessHandler()
+ }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Preview
+@Composable
+fun AppAndroidPreview() {
+ App()
+}
+
diff --git a/composeApp/src/androidMain/kotlin/com/grtsinry43/chronosight/MainApplication.kt b/composeApp/src/androidMain/kotlin/com/grtsinry43/chronosight/MainApplication.kt
new file mode 100644
index 0000000..3663463
--- /dev/null
+++ b/composeApp/src/androidMain/kotlin/com/grtsinry43/chronosight/MainApplication.kt
@@ -0,0 +1,12 @@
+package com.grtsinry43.chronosight
+
+import android.app.Application
+
+class MainApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ // 初始化全局屏幕时长统计
+ initGlobalScreenTimeManager(this)
+ }
+}
+
diff --git a/composeApp/src/androidMain/res/values/strings.xml b/composeApp/src/androidMain/res/values/strings.xml
index fb3227b..b116f35 100644
--- a/composeApp/src/androidMain/res/values/strings.xml
+++ b/composeApp/src/androidMain/res/values/strings.xml
@@ -1,3 +1,3 @@
- Activity Analyzer
+ Chronosight
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt
deleted file mode 100644
index 97f6c2d..0000000
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt
+++ /dev/null
@@ -1,192 +0,0 @@
-package com.grtsinry43.activityanalyzer
-
-import androidx.compose.foundation.*
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.shape.CircleShape
-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.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import com.grtsinry43.activityanalyzer.screens.MobileAboutScreen
-import com.grtsinry43.activityanalyzer.screens.MobileAnalyticsScreen
-import com.grtsinry43.activityanalyzer.screens.MobileHomeScreen
-import com.grtsinry43.activityanalyzer.screens.MobileProfileScreen
-import com.grtsinry43.activityanalyzer.screens.MobileReportsScreen
-import com.grtsinry43.activityanalyzer.screens.MobileSettingsScreen
-import com.grtsinry43.activityanalyzer.theme.AppThemes
-import kotlinx.coroutines.launch
-
-// --- Main App Composable ---
-@Composable
-@ExperimentalMaterial3Api
-fun App() {
- var isDarkTheme by remember { mutableStateOf(false) }
- val currentColors = if (isDarkTheme) AppThemes.DarkThemeColors else AppThemes.LightThemeColors
-
- // Navigation state
- var selectedNavItemIndex by remember { mutableStateOf(0) }
- val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
- val scope = rememberCoroutineScope()
-
- val navItems = listOf(
- "Home" to Icons.Default.Home,
- "Analytics" to Icons.Default.BarChart,
- "Reports" to Icons.Default.Assessment,
- "Profile" to Icons.Default.AccountCircle,
- "Settings" to Icons.Default.Settings,
- "About" to Icons.Default.Info
- )
-
- val currentScreenTitle = navItems[selectedNavItemIndex].first
-
- MaterialTheme(
- colorScheme = if (isDarkTheme) darkColorScheme(
- primary = currentColors.accent,
- secondary = currentColors.accentVariant,
- background = currentColors.background,
- surface = currentColors.surface,
- onPrimary = currentColors.onAccent,
- onSecondary = currentColors.onAccent,
- onBackground = currentColors.onBackground,
- onSurface = currentColors.onSurface,
- ) else lightColorScheme(
- primary = currentColors.accent,
- secondary = currentColors.accentVariant,
- background = currentColors.background,
- surface = currentColors.surface,
- onPrimary = currentColors.onAccent,
- onSecondary = currentColors.onAccent,
- onBackground = currentColors.onBackground,
- onSurface = currentColors.onSurface,
- )
- ) {
- ModalNavigationDrawer(
- drawerState = drawerState,
- drawerContent = {
- ModalDrawerSheet(
- drawerContainerColor = currentColors.surface,
- drawerContentColor = currentColors.onSurface
- ) {
- // Drawer Header with User Info
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .background(currentColors.accent.copy(alpha = 0.1f))
- .padding(16.dp),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Icon(
- imageVector = Icons.Filled.AccountCircle,
- contentDescription = "User Avatar",
- tint = currentColors.accent,
- modifier = Modifier
- .size(64.dp)
- .clip(CircleShape)
- )
- Spacer(modifier = Modifier.height(8.dp))
- Text(
- text = "grtsinry43", // Placeholder Nickname
- color = currentColors.onSurface,
- fontSize = 16.sp,
- fontWeight = FontWeight.Medium
- )
- Text(
- text = "grtsinry43@outlook.com", // Placeholder Email
- color = currentColors.secondaryText,
- fontSize = 12.sp
- )
- }
- Spacer(Modifier.height(12.dp))
- // Navigation Items
- navItems.forEachIndexed { index, item ->
- NavigationDrawerItem(
- icon = {
- Icon(
- item.second,
- contentDescription = item.first,
- tint = if (selectedNavItemIndex == index) currentColors.accent else currentColors.onSurface.copy(
- alpha = 0.7f
- )
- )
- },
- label = {
- Text(
- item.first,
- color = if (selectedNavItemIndex == index) currentColors.accent else currentColors.onSurface.copy(
- alpha = 0.7f
- )
- )
- },
- selected = selectedNavItemIndex == index,
- onClick = {
- selectedNavItemIndex = index
- scope.launch { drawerState.close() }
- },
- modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding),
- colors = NavigationDrawerItemDefaults.colors(
- selectedContainerColor = currentColors.accent.copy(alpha = 0.1f),
- unselectedContainerColor = Color.Transparent,
- selectedTextColor = currentColors.accent,
- unselectedTextColor = currentColors.onSurface.copy(alpha = 0.7f),
- selectedIconColor = currentColors.accent,
- unselectedIconColor = currentColors.onSurface.copy(alpha = 0.7f)
- )
- )
- }
- }
- }
- ) {
- Scaffold(
- containerColor = currentColors.background,
- topBar = {
- TopAppBar(
- title = { Text(currentScreenTitle, color = currentColors.onSurface) },
- navigationIcon = {
- IconButton(onClick = { scope.launch { drawerState.open() } }) {
- Icon(
- Icons.Filled.Menu,
- contentDescription = "Open Navigation Drawer",
- tint = currentColors.onSurface
- )
- }
- },
- colors = TopAppBarDefaults.topAppBarColors(
- containerColor = currentColors.surface,
- titleContentColor = currentColors.onSurface
- )
- )
- }
- ) { paddingValues ->
- Box(
- modifier = Modifier.padding(paddingValues).fillMaxSize()
- .background(currentColors.background)
- ) {
- when (selectedNavItemIndex) {
- 0 -> MobileHomeScreen(colors = currentColors)
- 1 -> MobileAnalyticsScreen(colors = currentColors)
- 2 -> MobileReportsScreen(colors = currentColors)
- 3 -> MobileProfileScreen(colors = currentColors)
- 4 -> MobileSettingsScreen(
- colors = currentColors,
- isDarkTheme = isDarkTheme,
- onThemeChange = { isDarkTheme = it }
- )
-
- 5 -> MobileAboutScreen(colors = currentColors)
- }
- }
- }
- }
- }
-}
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/App.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/App.kt
new file mode 100644
index 0000000..45b5f67
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/App.kt
@@ -0,0 +1,119 @@
+package com.grtsinry43.chronosight
+
+import androidx.compose.foundation.*
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.grtsinry43.chronosight.screens.MobileAboutScreen
+import com.grtsinry43.chronosight.screens.MobileAnalyticsScreen
+import com.grtsinry43.chronosight.screens.MobileHomeScreen
+import com.grtsinry43.chronosight.screens.MobileProfileScreen
+import com.grtsinry43.chronosight.screens.MobileReportsScreen
+import com.grtsinry43.chronosight.screens.MobileSettingsScreen
+import com.grtsinry43.chronosight.theme.AppThemes
+import dev.icerock.moko.resources.compose.*
+import com.grtsinry43.chronosight.MR
+
+@Composable
+@ExperimentalMaterial3Api
+fun App(onRequestUsageAccess: (() -> Unit)? = null) {
+ var isDarkTheme by remember { mutableStateOf(false) }
+ val currentColors = if (isDarkTheme) AppThemes.DarkThemeColors else AppThemes.LightThemeColors
+
+ // Navigation state
+ var selectedNavItemIndex by remember { mutableStateOf(0) }
+ val scope = rememberCoroutineScope()
+
+ val navItems = listOf(
+ stringResource(MR.strings.home) to Icons.Default.Home,
+ stringResource(MR.strings.analytics) to Icons.Default.BarChart,
+ stringResource(MR.strings.reports) to Icons.Default.Assessment,
+ stringResource(MR.strings.profile) to Icons.Default.AccountCircle,
+ stringResource(MR.strings.settings) to Icons.Default.Settings,
+ stringResource(MR.strings.about) to Icons.Default.Info
+ )
+
+ val currentScreenTitle = navItems[selectedNavItemIndex].first
+ val isAndroid = Platform.isAndroid
+
+ MaterialTheme(
+ colorScheme = if (isDarkTheme) darkColorScheme(
+ primary = currentColors.accent,
+ secondary = currentColors.accentVariant,
+ background = currentColors.background,
+ surface = currentColors.surface,
+ onPrimary = currentColors.onAccent,
+ onSecondary = currentColors.onAccent,
+ onBackground = currentColors.onBackground,
+ onSurface = currentColors.onSurface,
+ ) else lightColorScheme(
+ primary = currentColors.accent,
+ secondary = currentColors.accentVariant,
+ background = currentColors.background,
+ surface = currentColors.surface,
+ onPrimary = currentColors.onAccent,
+ onSecondary = currentColors.onAccent,
+ onBackground = currentColors.onBackground,
+ onSurface = currentColors.onSurface,
+ )
+ ) {
+ if (isAndroid) {
+ Scaffold(
+ containerColor = currentColors.background,
+ topBar = {
+ TopAppBar(
+ title = { Text(currentScreenTitle, color = currentColors.onSurface) },
+ colors = TopAppBarDefaults.topAppBarColors(
+ containerColor = currentColors.surface,
+ titleContentColor = currentColors.onSurface
+ )
+ )
+ },
+ bottomBar = {
+ NavigationBar(containerColor = currentColors.surface) {
+ navItems.forEachIndexed { idx, item ->
+ NavigationBarItem(
+ selected = selectedNavItemIndex == idx,
+ onClick = { selectedNavItemIndex = idx },
+ icon = { Icon(item.second, contentDescription = item.first) },
+ label = { Text(item.first) },
+ colors = NavigationBarItemDefaults.colors(
+ selectedIconColor = currentColors.accent,
+ selectedTextColor = currentColors.accent,
+ indicatorColor = currentColors.accent.copy(alpha = 0.12f)
+ )
+ )
+ }
+ }
+ }
+ ) { paddingValues ->
+ Box(
+ modifier = Modifier.padding(paddingValues).fillMaxSize()
+ .background(currentColors.background)
+ ) {
+ when (selectedNavItemIndex) {
+ 0 -> MobileHomeScreen(colors = currentColors, onRequestUsageAccess = onRequestUsageAccess)
+ 1 -> MobileAnalyticsScreen(colors = currentColors)
+ 2 -> MobileReportsScreen(colors = currentColors)
+ 3 -> MobileProfileScreen(colors = currentColors)
+ 4 -> MobileSettingsScreen(
+ colors = currentColors,
+ isDarkTheme = isDarkTheme,
+ onThemeChange = { isDarkTheme = it },
+ currentLocale = "en", // 仅用于设置页面UI
+ onLocaleChange = { /* 仅UI切换,不影响App语言 */ }
+ )
+ 5 -> MobileAboutScreen(colors = currentColors)
+ }
+ }
+ }
+ } else {
+ // TODO: 非Android平台的实现
+ }
+ }
+}
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ActivityItemC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/ActivityItemC.kt
similarity index 96%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ActivityItemC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/ActivityItemC.kt
index 78aef5e..70dfc83 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ActivityItemC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/ActivityItemC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.components
+package com.grtsinry43.chronosight.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -17,7 +17,7 @@ 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 com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ChartPlaceholderC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/ChartPlaceholderC.kt
similarity index 94%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ChartPlaceholderC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/ChartPlaceholderC.kt
index 29cf698..b20d65b 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ChartPlaceholderC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/ChartPlaceholderC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.components
+package com.grtsinry43.chronosight.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -14,7 +14,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/InfoItemC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/InfoItemC.kt
similarity index 96%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/InfoItemC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/InfoItemC.kt
index 1999093..e870de1 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/InfoItemC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/InfoItemC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.components
+package com.grtsinry43.chronosight.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -16,7 +16,7 @@ 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 com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/MetricCardC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/MetricCardC.kt
similarity index 95%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/MetricCardC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/MetricCardC.kt
index aace9a2..7180159 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/MetricCardC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/MetricCardC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.components
+package com.grtsinry43.chronosight.components
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
@@ -13,7 +13,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ReportItemC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/ReportItemC.kt
similarity index 96%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ReportItemC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/ReportItemC.kt
index fb5cb9a..c4e5764 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ReportItemC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/ReportItemC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.components
+package com.grtsinry43.chronosight.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -16,7 +16,7 @@ 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 com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SettingItemC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/SettingItemC.kt
similarity index 97%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SettingItemC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/SettingItemC.kt
index 5affdd2..16e7507 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SettingItemC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/SettingItemC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.components
+package com.grtsinry43.chronosight.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -13,7 +13,7 @@ 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 com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SimpleListItemC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/SimpleListItemC.kt
similarity index 94%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SimpleListItemC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/SimpleListItemC.kt
index e42ae1f..51fe925 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SimpleListItemC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/SimpleListItemC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.components
+package com.grtsinry43.chronosight.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
@@ -15,7 +15,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledButtonC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/StyledButtonC.kt
similarity index 95%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledButtonC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/StyledButtonC.kt
index 4b19506..910300f 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledButtonC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/StyledButtonC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.components
+package com.grtsinry43.chronosight.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.*
@@ -13,7 +13,7 @@ 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 com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledCard.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/StyledCard.kt
similarity index 92%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledCard.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/StyledCard.kt
index 518ffa2..456e05a 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledCard.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/components/StyledCard.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.components
+package com.grtsinry43.chronosight.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.ColumnScope
@@ -11,7 +11,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAboutScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileAboutScreenC.kt
similarity index 95%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAboutScreenC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileAboutScreenC.kt
index 251e626..9f0a54b 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAboutScreenC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileAboutScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
@@ -14,9 +14,9 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import com.grtsinry43.activityanalyzer.components.InfoItem
-import com.grtsinry43.activityanalyzer.components.SettingItem
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.InfoItem
+import com.grtsinry43.chronosight.components.SettingItem
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
@@ -36,7 +36,7 @@ fun MobileAboutScreen(colors: AppThemes.Colors) {
modifier = Modifier.size(72.dp) // Slightly smaller for mobile about screen
)
Text(
- "Activity Analyzer",
+ "Chronosight",
style = MaterialTheme.typography.headlineMedium.copy(
color = colors.onBackground,
fontWeight = FontWeight.Bold,
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAnalyticsScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileAnalyticsScreenC.kt
similarity index 51%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAnalyticsScreenC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileAnalyticsScreenC.kt
index 9c082ed..a81348d 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAnalyticsScreenC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileAnalyticsScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -11,18 +11,40 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import com.grtsinry43.activityanalyzer.components.ChartPlaceholder
-import com.grtsinry43.activityanalyzer.components.MetricCard
-import com.grtsinry43.activityanalyzer.components.StyledButton
-import com.grtsinry43.activityanalyzer.components.StyledCard
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.ChartPlaceholder
+import com.grtsinry43.chronosight.components.MetricCard
+import com.grtsinry43.chronosight.components.StyledButton
+import com.grtsinry43.chronosight.components.StyledCard
+import com.grtsinry43.chronosight.formatMillisToHourMin
+import com.grtsinry43.chronosight.theme.AppThemes
+import com.grtsinry43.chronosight.getScreenTime
+import com.grtsinry43.chronosight.getAppUsageStats
+import com.grtsinry43.chronosight.AppUsageInfo
+import kotlinx.coroutines.delay
import org.jetbrains.compose.ui.tooling.preview.Preview
+import java.util.Calendar
+import java.util.Locale
+import dev.icerock.moko.resources.compose.stringResource
+import com.grtsinry43.chronosight.MR
@Composable
fun MobileAnalyticsScreen(colors: AppThemes.Colors) {
- var selectedTimeRange by remember { mutableStateOf("Last 7 Days") }
- val timeRanges =
- listOf("Today", "Yesterday", "Last 7 Days", "Last 30 Days") // Simplified for mobile
+ // Get string values inside the composable scope
+ val todayStr = stringResource(MR.strings.today)
+ val yesterdayStr = stringResource(MR.strings.yesterday)
+ val last7DaysStr = stringResource(MR.strings.last_7_days)
+ val last30DaysStr = stringResource(MR.strings.last_30_days)
+ var selectedTimeRange by remember { mutableStateOf(last7DaysStr) }
+ val timeRanges = listOf(todayStr, yesterdayStr, last7DaysStr, last30DaysStr)
+
+ // 动态获取全局屏幕使用时长(每秒刷新一次)
+ val screenTimeMillis = remember { mutableStateOf(getScreenTime().getUsageMillis()) }
+ LaunchedEffect(Unit) {
+ while (true) {
+ screenTimeMillis.value = getScreenTime().getUsageMillis()
+ delay(1000)
+ }
+ }
Column(
modifier = Modifier
@@ -32,7 +54,7 @@ fun MobileAnalyticsScreen(colors: AppThemes.Colors) {
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
- "Screen Time Analytics",
+ stringResource(MR.strings.screen_time_analytics),
style = MaterialTheme.typography.headlineSmall.copy(
color = colors.onBackground,
fontWeight = FontWeight.Bold,
@@ -77,45 +99,59 @@ fun MobileAnalyticsScreen(colors: AppThemes.Colors) {
// Key Metrics - Use a Grid for better mobile layout if more than 2, or stacked Rows
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
MetricCard(
- title = "Total Time",
- value = "25h 10m",
+ title = stringResource(MR.strings.total_screen_on_time),
+ value = formatMillisToHourMin(screenTimeMillis.value),
icon = Icons.Default.Smartphone,
colors = colors,
modifier = Modifier.weight(1f)
)
MetricCard(
- title = "Avg Daily",
- value = "3h 35m",
+ title = stringResource(MR.strings.avg_daily),
+ value = formatMillisToHourMin(screenTimeMillis.value),
icon = Icons.Default.AvTimer,
colors = colors,
modifier = Modifier.weight(1f)
)
}
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
+ val now = remember { System.currentTimeMillis() }
+ val todayStart = remember {
+ Calendar.getInstance().apply {
+ set(Calendar.HOUR_OF_DAY, 0)
+ set(Calendar.MINUTE, 0)
+ set(Calendar.SECOND, 0)
+ set(Calendar.MILLISECOND, 0)
+ }.timeInMillis
+ }
+ val appStats = remember { mutableStateListOf() }
+ LaunchedEffect(selectedTimeRange) {
+ appStats.clear()
+ appStats.addAll(getAppUsageStats(todayStart, now))
+ }
+ val mostUsed = appStats.maxByOrNull { it.usageMillis }
MetricCard(
- title = "Most Used",
- value = "App A",
+ title = stringResource(MR.strings.most_used),
+ value = mostUsed?.appName ?: "-",
icon = Icons.Default.StarOutline,
colors = colors,
modifier = Modifier.weight(1f)
)
MetricCard(
- title = "Pickups",
- value = "75",
+ title = stringResource(MR.strings.pickups),
+ value = "-",
icon = Icons.Default.TouchApp,
colors = colors,
modifier = Modifier.weight(1f)
)
}
-
StyledCard(colors = colors) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
- "Usage Patterns",
+ stringResource(MR.strings.usage_patterns),
style = MaterialTheme.typography.titleMedium.copy(
color = colors.onSurface,
fontWeight = FontWeight.SemiBold,
@@ -123,25 +159,71 @@ fun MobileAnalyticsScreen(colors: AppThemes.Colors) {
)
)
ChartPlaceholder(
- text = "Daily Screen Time (Bar Chart)",
+ text = stringResource(MR.strings.daily_screen_time_chart),
colors = colors,
modifier = Modifier.fillMaxWidth().height(180.dp)
) // Slightly smaller charts for mobile
ChartPlaceholder(
- text = "App Usage (Pie Chart)",
+ text = stringResource(MR.strings.app_usage_pie_chart),
colors = colors,
modifier = Modifier.fillMaxWidth().height(180.dp)
)
}
}
+ // --- 应用使用时长排行 ---
+ Spacer(modifier = Modifier.height(16.dp))
+ StyledCard(colors = colors) {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text(
+ text = stringResource(MR.strings.app_usage_breakdown),
+ style = MaterialTheme.typography.titleMedium.copy(
+ color = colors.onSurface,
+ fontWeight = FontWeight.SemiBold,
+ fontSize = 16.sp
+ )
+ )
+ // 获取今天0点到现在的各应用时长
+ val now = remember { System.currentTimeMillis() }
+ val todayStart = remember {
+ Calendar.getInstance().apply {
+ set(Calendar.HOUR_OF_DAY, 0)
+ set(Calendar.MINUTE, 0)
+ set(Calendar.SECOND, 0)
+ set(Calendar.MILLISECOND, 0)
+ }.timeInMillis
+ }
+ val appStats = remember { mutableStateListOf() }
+ LaunchedEffect(selectedTimeRange) {
+ // 这里只做今日,后续可按时间段切换
+ appStats.clear()
+ appStats.addAll(getAppUsageStats(todayStart, now))
+ }
+ if (appStats.isEmpty()) {
+ Text(stringResource(MR.strings.no_usage_data), color = colors.secondaryText)
+ } else {
+ Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ appStats.take(10).forEachIndexed { idx, info ->
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text("${idx + 1}. ${info.appName}", color = colors.onSurface)
+ Text(formatMillisToHourMin(info.usageMillis), color = colors.accent)
+ }
+ }
+ }
+ }
+ }
+ }
+
StyledCard(colors = colors) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
- "Analysis Tools",
+ stringResource(MR.strings.analysis_tools),
style = MaterialTheme.typography.titleMedium.copy(
color = colors.onSurface,
fontWeight = FontWeight.SemiBold,
@@ -151,21 +233,21 @@ fun MobileAnalyticsScreen(colors: AppThemes.Colors) {
StyledButton(
modifier = Modifier.fillMaxWidth(),
icon = Icons.Default.PieChartOutline,
- text = "App Usage Breakdown",
+ text = stringResource(MR.strings.app_usage_breakdown_btn),
onClick = { /* TODO */ },
colors = colors
)
StyledButton(
modifier = Modifier.fillMaxWidth(),
icon = Icons.Default.AccessTime,
- text = "Time of Day Analysis",
+ text = stringResource(MR.strings.time_of_day_analysis),
onClick = { /* TODO */ },
colors = colors
)
StyledButton(
modifier = Modifier.fillMaxWidth(),
icon = Icons.Default.TrackChanges,
- text = "Set Usage Goals",
+ text = stringResource(MR.strings.set_usage_goals),
onClick = { /* TODO */ },
colors = colors
)
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileHomeScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileHomeScreenC.kt
similarity index 57%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileHomeScreenC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileHomeScreenC.kt
index 5892949..d87aa22 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileHomeScreenC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileHomeScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -6,21 +6,39 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
-import androidx.compose.runtime.Composable
+import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import com.grtsinry43.activityanalyzer.components.ActivityItem
-import com.grtsinry43.activityanalyzer.components.SimpleListItem
-import com.grtsinry43.activityanalyzer.components.StyledButton
-import com.grtsinry43.activityanalyzer.components.StyledCard
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.ActivityItem
+import com.grtsinry43.chronosight.components.SimpleListItem
+import com.grtsinry43.chronosight.components.StyledButton
+import com.grtsinry43.chronosight.components.StyledCard
+import com.grtsinry43.chronosight.formatMillisToHourMin
+import com.grtsinry43.chronosight.theme.AppThemes
+import com.grtsinry43.chronosight.getScreenTime
+import com.grtsinry43.chronosight.getAppUsageStats
+import com.grtsinry43.chronosight.AppUsageInfo
+import kotlinx.coroutines.delay
import org.jetbrains.compose.ui.tooling.preview.Preview
+import java.util.Calendar
+import java.util.Locale
+import dev.icerock.moko.resources.compose.stringResource
+import com.grtsinry43.chronosight.MR
@Composable
-fun MobileHomeScreen(colors: AppThemes.Colors) {
+fun MobileHomeScreen(colors: AppThemes.Colors, onRequestUsageAccess: (() -> Unit)? = null) {
+ // 动态获取全局屏幕使用时长(每秒刷新一次)
+ val screenTimeMillis = remember { mutableStateOf(getScreenTime().getUsageMillis()) }
+ LaunchedEffect(Unit) {
+ while (true) {
+ screenTimeMillis.value = getScreenTime().getUsageMillis()
+ delay(1000)
+ }
+ }
+
Column(
modifier = Modifier
.fillMaxSize()
@@ -29,7 +47,7 @@ fun MobileHomeScreen(colors: AppThemes.Colors) {
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
- text = "Welcome Back, grtsinry43!",
+ text = stringResource(MR.strings.welcome, "grtsinry43"),
style = MaterialTheme.typography.headlineSmall.copy(
color = colors.onBackground,
fontWeight = FontWeight.Bold,
@@ -43,7 +61,7 @@ fun MobileHomeScreen(colors: AppThemes.Colors) {
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
- text = "Today's Screen Time",
+ text = stringResource(MR.strings.today_screen_time),
style = MaterialTheme.typography.titleLarge.copy( // Larger title for emphasis
color = colors.onSurface,
fontWeight = FontWeight.SemiBold,
@@ -51,7 +69,7 @@ fun MobileHomeScreen(colors: AppThemes.Colors) {
)
)
Text(
- text = "3h 45m", // Placeholder
+ text = formatMillisToHourMin(screenTimeMillis.value),
style = MaterialTheme.typography.displayMedium.copy( // Prominent display
color = colors.accent,
fontWeight = FontWeight.Bold,
@@ -60,7 +78,7 @@ fun MobileHomeScreen(colors: AppThemes.Colors) {
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Text(
- text = "You're on track with your daily goal!", // Placeholder
+ text = stringResource(MR.strings.on_track), // Placeholder
style = MaterialTheme.typography.bodyMedium.copy(
color = colors.secondaryText,
fontSize = 14.sp
@@ -76,24 +94,49 @@ fun MobileHomeScreen(colors: AppThemes.Colors) {
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
- text = "Quick Glance: Top Apps",
+ text = stringResource(MR.strings.quick_glance),
style = MaterialTheme.typography.titleMedium.copy(
color = colors.onSurface,
fontWeight = FontWeight.SemiBold,
fontSize = 16.sp
)
)
- SimpleListItem(
- icon = Icons.Filled.SmartDisplay,
- text = "App A: 1h 15m",
- colors = colors
- )
- SimpleListItem(
- icon = Icons.Filled.PhotoCamera,
- text = "App B: 45m",
- colors = colors
- )
- SimpleListItem(icon = Icons.Filled.Chat, text = "App C: 30m", colors = colors)
+ // 动态获取今日前3应用
+ val now = remember { System.currentTimeMillis() }
+ val todayStart = remember {
+ Calendar.getInstance().apply {
+ set(Calendar.HOUR_OF_DAY, 0)
+ set(Calendar.MINUTE, 0)
+ set(Calendar.SECOND, 0)
+ set(Calendar.MILLISECOND, 0)
+ }.timeInMillis
+ }
+ val appStats = remember { mutableStateListOf() }
+ LaunchedEffect(Unit) {
+ appStats.clear()
+ appStats.addAll(getAppUsageStats(todayStart, now))
+ }
+ if (appStats.isEmpty()) {
+ Text(stringResource(MR.strings.no_usage_data), color = colors.secondaryText)
+ Spacer(Modifier.height(8.dp))
+ Button(
+ onClick = {
+ // 这里不能直接调用 android 相关代码,改为调用回调
+ onRequestUsageAccess?.invoke()
+ },
+ colors = ButtonDefaults.buttonColors(containerColor = colors.accent)
+ ) {
+ Text(stringResource(MR.strings.grant_usage_access), color = colors.onAccent)
+ }
+ } else {
+ appStats.take(3).forEach { info ->
+ SimpleListItem(
+ icon = Icons.Filled.SmartDisplay, // 可根据包名/进程名自定义图标
+ text = "${info.appName}: ${formatMillisToHourMin(info.usageMillis)}",
+ colors = colors
+ )
+ }
+ }
}
}
@@ -103,7 +146,7 @@ fun MobileHomeScreen(colors: AppThemes.Colors) {
verticalArrangement = Arrangement.spacedBy(12.dp) // Spacing for buttons
) {
Text(
- text = "Quick Actions",
+ text = stringResource(MR.strings.quick_actions),
style = MaterialTheme.typography.titleMedium.copy(
color = colors.onSurface,
fontWeight = FontWeight.SemiBold,
@@ -113,14 +156,14 @@ fun MobileHomeScreen(colors: AppThemes.Colors) {
StyledButton( // Full width buttons for mobile quick actions
modifier = Modifier.fillMaxWidth(),
icon = Icons.Default.HourglassTop,
- text = "Start Focus Session",
+ text = stringResource(MR.strings.start_focus),
onClick = { /* TODO */ },
colors = colors
)
StyledButton(
modifier = Modifier.fillMaxWidth(),
icon = Icons.Default.CalendarViewDay, // Changed icon
- text = "View Today's Details",
+ text = stringResource(MR.strings.view_today),
onClick = { /* TODO */ },
colors = colors
)
@@ -133,7 +176,7 @@ fun MobileHomeScreen(colors: AppThemes.Colors) {
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
- text = "Recent Insights",
+ text = stringResource(MR.strings.recent_insights),
style = MaterialTheme.typography.titleMedium.copy(
color = colors.onSurface,
fontWeight = FontWeight.SemiBold,
@@ -141,13 +184,13 @@ fun MobileHomeScreen(colors: AppThemes.Colors) {
)
)
ActivityItem(
- title = "Exceeded daily goal for App X.",
+ title = stringResource(MR.strings.exceeded_goal),
time = "Today, 2:30 PM",
icon = Icons.Default.WarningAmber,
colors = colors
)
ActivityItem(
- title = "Screen time 20% higher yesterday.",
+ title = stringResource(MR.strings.higher_yesterday),
time = "Insight from yesterday",
icon = Icons.Default.TrendingUp,
colors = colors
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileProfileScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileProfileScreenC.kt
similarity index 95%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileProfileScreenC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileProfileScreenC.kt
index a502a84..d3c8ed7 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileProfileScreenC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileProfileScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
@@ -16,10 +16,10 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import com.grtsinry43.activityanalyzer.components.SimpleListItem
-import com.grtsinry43.activityanalyzer.components.StyledButton
-import com.grtsinry43.activityanalyzer.components.StyledCard
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.SimpleListItem
+import com.grtsinry43.chronosight.components.StyledButton
+import com.grtsinry43.chronosight.components.StyledCard
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileReportsScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileReportsScreenC.kt
similarity index 95%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileReportsScreenC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileReportsScreenC.kt
index d911a50..edf46fc 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileReportsScreenC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileReportsScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -12,8 +12,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import com.grtsinry43.activityanalyzer.components.ReportItem
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.ReportItem
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileSettingsScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileSettingsScreenC.kt
similarity index 76%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileSettingsScreenC.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileSettingsScreenC.kt
index 70911d4..70de498 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileSettingsScreenC.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/screens/MobileSettingsScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
@@ -12,18 +12,23 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import com.grtsinry43.activityanalyzer.components.SettingItem
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.SettingItem
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
fun MobileSettingsScreen(
colors: AppThemes.Colors,
isDarkTheme: Boolean,
- onThemeChange: (Boolean) -> Unit
+ onThemeChange: (Boolean) -> Unit,
+ currentLocale: String,
+ onLocaleChange: (String) -> Unit
) {
var autoBackupEnabled by remember { mutableStateOf(true) }
var notificationsEnabled by remember { mutableStateOf(true) }
+ val languageOptions = listOf("简体中文" to "zh", "English" to "en")
+ var languageMenuExpanded by remember { mutableStateOf(false) }
+ val currentLanguageName = languageOptions.find { it.second == currentLocale }?.first ?: "English"
Column(
modifier = Modifier
@@ -34,6 +39,31 @@ fun MobileSettingsScreen(
) {
// General Group
SettingsGroupHeader(title = "General", colors = colors)
+ SettingItem(
+ title = "Language",
+ subtitle = currentLanguageName,
+ icon = Icons.Default.Language,
+ colors = colors,
+ onClick = { languageMenuExpanded = true }
+ )
+ DropdownMenu(
+ expanded = languageMenuExpanded,
+ onDismissRequest = { languageMenuExpanded = false }
+ ) {
+ languageOptions.forEach { (name, code) ->
+ DropdownMenuItem(
+ text = { Text(name) },
+ onClick = {
+ onLocaleChange(code)
+ languageMenuExpanded = false
+ }
+ )
+ }
+ }
+ Divider(
+ color = colors.border.copy(alpha = 0.2f),
+ modifier = Modifier.padding(horizontal = 16.dp)
+ )
SettingItem(
title = "Dark Theme",
subtitle = if (isDarkTheme) "Enabled" else "Disabled",
@@ -169,7 +199,13 @@ fun SettingsGroupHeader(title: String, colors: AppThemes.Colors) {
@Composable
fun MobileSettingsScreenPreview() {
MaterialTheme {
- MobileSettingsScreen(colors = AppThemes.LightThemeColors, isDarkTheme = false, onThemeChange = {})
+ MobileSettingsScreen(
+ colors = AppThemes.LightThemeColors,
+ isDarkTheme = false,
+ onThemeChange = {},
+ currentLocale = "en",
+ onLocaleChange = {}
+ )
}
}
@@ -177,6 +213,12 @@ fun MobileSettingsScreenPreview() {
@Composable
fun MobileSettingsScreenDarkPreview() {
MaterialTheme {
- MobileSettingsScreen(colors = AppThemes.DarkThemeColors, isDarkTheme = true, onThemeChange = {})
+ MobileSettingsScreen(
+ colors = AppThemes.DarkThemeColors,
+ isDarkTheme = true,
+ onThemeChange = {},
+ currentLocale = "en",
+ onLocaleChange = {}
+ )
}
}
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/theme/AppThemes.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/theme/AppThemes.kt
similarity index 97%
rename from composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/theme/AppThemes.kt
rename to composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/theme/AppThemes.kt
index 5fc7136..0a67262 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/theme/AppThemes.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/chronosight/theme/AppThemes.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.theme
+package com.grtsinry43.chronosight.theme
import androidx.compose.ui.graphics.Color
diff --git a/composeApp/src/commonMain/resources/MR/base/strings.xml b/composeApp/src/commonMain/resources/MR/base/strings.xml
deleted file mode 100644
index ba4f33b..0000000
--- a/composeApp/src/commonMain/resources/MR/base/strings.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
- Activity Analyzer
- Home
- Analytics
- Reports
- Settings
- About
- Quick Actions
- New Analysis
- Import Data
- Recent Activities
- Analysis Report
- Data Import
- Analysis Tools
- Time Analysis
- Distribution Analysis
- My Reports
- Monthly Activity Report
- Weekly Activity Report
- Download
- Data Storage Location
- Default Location
- Change
- Auto Backup
- Daily
- Notifications
- Enabled
- Version
- A powerful activity analysis tool to help you better understand and optimize your activity data.
- Check Updates
-
\ No newline at end of file
diff --git a/composeApp/src/commonMain/resources/MR/zh/strings.xml b/composeApp/src/commonMain/resources/MR/zh/strings.xml
deleted file mode 100644
index cd7cb67..0000000
--- a/composeApp/src/commonMain/resources/MR/zh/strings.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
- 活动分析器
- 首页
- 数据分析
- 报告
- 设置
- 关于
- 快速操作
- 新建分析
- 导入数据
- 最近活动
- 数据分析报告
- 活动数据导入
- 分析工具
- 时间分析
- 分布分析
- 我的报告
- 月度活动报告
- 周活动分析
- 下载
- 数据存储位置
- 默认位置
- 更改
- 自动备份
- 每天
- 通知设置
- 开启
- 版本
- 一个强大的活动分析工具,帮助您更好地理解和优化您的活动数据。
- 检查更新
-
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/DesktopApp.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/DesktopApp.kt
similarity index 93%
rename from composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/DesktopApp.kt
rename to composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/DesktopApp.kt
index 1b0ffa2..744ec2e 100644
--- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/DesktopApp.kt
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/DesktopApp.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer
+package com.grtsinry43.chronosight
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
@@ -17,14 +17,14 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.jetbrains.compose.ui.tooling.preview.Preview
import androidx.compose.foundation.text.selection.SelectionContainer
-import com.grtsinry43.activityanalyzer.components.*
-import com.grtsinry43.activityanalyzer.screens.AboutScreen
-import com.grtsinry43.activityanalyzer.screens.AnalyticsScreen
-import com.grtsinry43.activityanalyzer.screens.HomeScreen
-import com.grtsinry43.activityanalyzer.screens.ProfileScreen
-import com.grtsinry43.activityanalyzer.screens.ReportsScreen
-import com.grtsinry43.activityanalyzer.screens.SettingsScreen
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.*
+import com.grtsinry43.chronosight.screens.AboutScreen
+import com.grtsinry43.chronosight.screens.AnalyticsScreen
+import com.grtsinry43.chronosight.screens.HomeScreen
+import com.grtsinry43.chronosight.screens.ProfileScreen
+import com.grtsinry43.chronosight.screens.ReportsScreen
+import com.grtsinry43.chronosight.screens.SettingsScreen
+import com.grtsinry43.chronosight.theme.AppThemes
@Composable
@Preview
@@ -64,7 +64,7 @@ fun DesktopApp() {
) {
if (!isSidebarCollapsed) {
Text(
- "Activity Analyzer", // 应用标题
+ "Chronosight", // 应用标题
fontSize = 18.sp, // 字体大小
fontWeight = FontWeight.SemiBold, // 字体粗细
color = currentColors.onSurface, // 文本颜色
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/components/NavItemC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/components/NavItemC.kt
similarity index 97%
rename from composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/components/NavItemC.kt
rename to composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/components/NavItemC.kt
index 6d322ef..acde9ae 100644
--- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/components/NavItemC.kt
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/components/NavItemC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.components
+package com.grtsinry43.chronosight.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@@ -20,7 +20,7 @@ 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 com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/main.kt
similarity index 95%
rename from composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt
rename to composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/main.kt
index 5e27ecf..2980d37 100644
--- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/main.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer
+package com.grtsinry43.chronosight
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
@@ -50,7 +50,7 @@ fun main() = application {
Tray(
state = trayState,
icon = trayIconPainter ?: BitmapPainter(createDefaultTrayIcon()), // 如果自定义图标加载失败,使用一个备用图标
- tooltip = "Activity Analyzer", // 鼠标悬停在托盘图标上时显示的提示文字
+ tooltip = "Chronosight", // 鼠标悬停在托盘图标上时显示的提示文字
menu = {
// 托盘菜单项
Item(
@@ -82,7 +82,7 @@ fun main() = application {
// exitApplication()
},
state = windowState, // 应用窗口状态
- title = "Activity Analyzer",
+ title = "Chronosight",
resizable = true, // 允许用户调整窗口大小 (默认为 true)
// icon = painterResource("app_icon.png") // 可选:设置窗口左上角的图标和任务栏图标
) {
@@ -111,7 +111,7 @@ private fun createDefaultTrayIcon(): ImageBitmap { // Change return type to Imag
g.color = java.awt.Color.BLUE
g.fillRect(0, 0, width, height)
g.color = java.awt.Color.WHITE
- g.drawString("AA", 20, 40) // "Activity Analyzer" 缩写
+ g.drawString("AA", 20, 40) // "Chronosight" 缩写
} finally {
g.dispose()
}
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AboutScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/AboutScreenC.kt
similarity index 95%
rename from composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AboutScreenC.kt
rename to composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/AboutScreenC.kt
index 6da697c..bb26207 100644
--- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AboutScreenC.kt
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/AboutScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
-import com.grtsinry43.activityanalyzer.components.InfoItem
-import com.grtsinry43.activityanalyzer.components.SettingItem
-import com.grtsinry43.activityanalyzer.components.StyledCard
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.InfoItem
+import com.grtsinry43.chronosight.components.SettingItem
+import com.grtsinry43.chronosight.components.StyledCard
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
@@ -35,7 +35,7 @@ fun AboutScreen(colors: AppThemes.Colors) {
modifier = Modifier.size(80.dp) // 图标大小
)
Text(
- "Activity Analyzer", // 应用名称
+ "Chronosight", // 应用名称
style = MaterialTheme.typography.headlineMedium.copy(
color = colors.onBackground,
fontWeight = FontWeight.Bold
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AnalyticsScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/AnalyticsScreenC.kt
similarity index 66%
rename from composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AnalyticsScreenC.kt
rename to composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/AnalyticsScreenC.kt
index 4c111e9..5ca15f3 100644
--- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AnalyticsScreenC.kt
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/AnalyticsScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -12,11 +12,17 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
-import com.grtsinry43.activityanalyzer.components.ChartPlaceholder
-import com.grtsinry43.activityanalyzer.components.MetricCard
-import com.grtsinry43.activityanalyzer.components.StyledButton
-import com.grtsinry43.activityanalyzer.components.StyledCard
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import androidx.compose.ui.unit.sp
+import com.grtsinry43.chronosight.components.ChartPlaceholder
+import com.grtsinry43.chronosight.components.MetricCard
+import com.grtsinry43.chronosight.components.StyledButton
+import com.grtsinry43.chronosight.components.StyledCard
+import com.grtsinry43.chronosight.theme.AppThemes
+import com.grtsinry43.chronosight.getAppUsageStats
+import com.grtsinry43.chronosight.AppUsageInfo
+import com.grtsinry43.chronosight.formatMillisToHourMin
+import com.grtsinry43.chronosight.getScreenTime
+import java.util.Calendar
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
@@ -84,36 +90,60 @@ fun AnalyticsScreen(colors: AppThemes.Colors) {
// Key Metrics Cards
// 关键指标卡片
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
+ // 动态获取全局屏幕使用时长(每秒刷新一次)
+ val screenTimeMillis = remember { mutableStateOf(getScreenTime().getUsageMillis()) }
+ LaunchedEffect(Unit) {
+ while (true) {
+ screenTimeMillis.value = getScreenTime().getUsageMillis()
+ kotlinx.coroutines.delay(1000)
+ }
+ }
MetricCard(
title = "Total Screen Time",
- value = "25h 10m", // 示例数据
+ value = formatMillisToHourMin(screenTimeMillis.value),
icon = Icons.Default.Smartphone,
colors = colors,
modifier = Modifier.weight(1f)
- ) // 总屏幕时间
+ )
MetricCard(
title = "Avg Daily Time",
- value = "3h 35m", // 示例数据
+ value = formatMillisToHourMin(screenTimeMillis.value),
icon = Icons.Default.AvTimer,
colors = colors,
modifier = Modifier.weight(1f)
- ) // 平均每日时间
+ )
}
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
+ // 今日最常用应用
+ val now = remember { System.currentTimeMillis() }
+ val todayStart = remember {
+ Calendar.getInstance().apply {
+ set(Calendar.HOUR_OF_DAY, 0)
+ set(Calendar.MINUTE, 0)
+ set(Calendar.SECOND, 0)
+ set(Calendar.MILLISECOND, 0)
+ }.timeInMillis
+ }
+ val appStats = remember { mutableStateListOf() }
+ LaunchedEffect(selectedTimeRange) {
+ appStats.clear()
+ appStats.addAll(getAppUsageStats(todayStart, now))
+ }
+ val mostUsed = appStats.maxByOrNull { it.usageMillis }
MetricCard(
title = "Most Used App",
- value = "App A (8h)", // 示例数据
+ value = mostUsed?.appName ?: "-",
icon = Icons.Default.StarOutline,
colors = colors,
modifier = Modifier.weight(1f)
- ) // 最常用应用
+ )
MetricCard(
title = "Pickups",
- value = "75 today", // 示例数据
+ value = "-",
icon = Icons.Default.TouchApp,
colors = colors,
modifier = Modifier.weight(1f)
- ) // 拿起次数
+ )
}
// Charts Section
@@ -148,6 +178,52 @@ fun AnalyticsScreen(colors: AppThemes.Colors) {
}
}
+ // --- 应用使用时长排行 ---
+ Spacer(modifier = Modifier.height(16.dp))
+ StyledCard(colors = colors) {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text(
+ text = "App Usage Breakdown",
+ style = MaterialTheme.typography.titleMedium.copy(
+ color = colors.onSurface,
+ fontWeight = FontWeight.SemiBold,
+ fontSize = 16.sp
+ )
+ )
+ // 获取今天0点到现在的各应用时长
+ val now = remember { System.currentTimeMillis() }
+ val todayStart = remember {
+ Calendar.getInstance().apply {
+ set(Calendar.HOUR_OF_DAY, 0)
+ set(Calendar.MINUTE, 0)
+ set(Calendar.SECOND, 0)
+ set(Calendar.MILLISECOND, 0)
+ }.timeInMillis
+ }
+ val appStats = remember { mutableStateListOf() }
+ LaunchedEffect(selectedTimeRange) {
+ // 这里只做今日,后续可按时间段切换
+ appStats.clear()
+ appStats.addAll(getAppUsageStats(todayStart, now))
+ }
+ if (appStats.isEmpty()) {
+ Text("No usage data.", color = colors.secondaryText)
+ } else {
+ Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ appStats.take(10).forEachIndexed { idx, info ->
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text("${idx + 1}. ${info.appName}", color = colors.onSurface)
+ Text(formatMillisToHourMin(info.usageMillis), color = colors.accent)
+ }
+ }
+ }
+ }
+ }
+ }
+
// Analysis Tools
// 分析工具
StyledCard(colors = colors) {
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/HomeScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/HomeScreenC.kt
similarity index 87%
rename from composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/HomeScreenC.kt
rename to composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/HomeScreenC.kt
index b6ddfd9..82a590f 100644
--- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/HomeScreenC.kt
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/HomeScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -7,16 +7,18 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
-import androidx.compose.runtime.Composable
+import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
-import com.grtsinry43.activityanalyzer.components.ActivityItem
-import com.grtsinry43.activityanalyzer.components.SimpleListItem
-import com.grtsinry43.activityanalyzer.components.StyledButton
-import com.grtsinry43.activityanalyzer.components.StyledCard
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.ActivityItem
+import com.grtsinry43.chronosight.components.SimpleListItem
+import com.grtsinry43.chronosight.components.StyledButton
+import com.grtsinry43.chronosight.components.StyledCard
+import com.grtsinry43.chronosight.getScreenTime
+import com.grtsinry43.chronosight.formatMillisToHourMin
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
@@ -41,6 +43,14 @@ fun HomeScreen(colors: AppThemes.Colors) {
// Overview of today's screen time
// 今日屏幕时间概览
StyledCard(colors = colors) {
+ // 动态获取屏幕时长,每秒刷新
+ val screenTimeMillis = remember { mutableStateOf(getScreenTime().getUsageMillis()) }
+ LaunchedEffect(Unit) {
+ while (true) {
+ screenTimeMillis.value = getScreenTime().getUsageMillis()
+ kotlinx.coroutines.delay(1000)
+ }
+ }
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
@@ -53,7 +63,7 @@ fun HomeScreen(colors: AppThemes.Colors) {
)
)
Text(
- text = "3h 45m", // 示例数据
+ text = formatMillisToHourMin(screenTimeMillis.value),
style = MaterialTheme.typography.displaySmall.copy(
color = colors.accent,
fontWeight = FontWeight.Bold
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ProfileScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/ProfileScreenC.kt
similarity index 96%
rename from composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ProfileScreenC.kt
rename to composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/ProfileScreenC.kt
index 047b090..27d4c05 100644
--- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ProfileScreenC.kt
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/ProfileScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
-import com.grtsinry43.activityanalyzer.components.SimpleListItem
-import com.grtsinry43.activityanalyzer.components.StyledButton
-import com.grtsinry43.activityanalyzer.components.StyledCard
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.SimpleListItem
+import com.grtsinry43.chronosight.components.StyledButton
+import com.grtsinry43.chronosight.components.StyledCard
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ReportsScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/ReportsScreenC.kt
similarity index 94%
rename from composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ReportsScreenC.kt
rename to composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/ReportsScreenC.kt
index 34800ca..37d16ab 100644
--- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ReportsScreenC.kt
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/ReportsScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -11,10 +11,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
-import com.grtsinry43.activityanalyzer.components.ReportItem
-import com.grtsinry43.activityanalyzer.components.StyledButton
-import com.grtsinry43.activityanalyzer.components.StyledCard
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.ReportItem
+import com.grtsinry43.chronosight.components.StyledButton
+import com.grtsinry43.chronosight.components.StyledCard
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/SettingsScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/SettingsScreenC.kt
similarity index 96%
rename from composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/SettingsScreenC.kt
rename to composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/SettingsScreenC.kt
index d0959a2..9414c22 100644
--- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/SettingsScreenC.kt
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/chronosight/screens/SettingsScreenC.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer.screens
+package com.grtsinry43.chronosight.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -10,9 +10,9 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
-import com.grtsinry43.activityanalyzer.components.SettingItem
-import com.grtsinry43.activityanalyzer.components.StyledCard
-import com.grtsinry43.activityanalyzer.theme.AppThemes
+import com.grtsinry43.chronosight.components.SettingItem
+import com.grtsinry43.chronosight.components.StyledCard
+import com.grtsinry43.chronosight.theme.AppThemes
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
@@ -68,7 +68,7 @@ fun SettingsScreen(
Divider(color = colors.border.copy(alpha = 0.3f)) // 分隔线
SettingItem( // 数据存储位置
title = "Data Storage Location", // 数据存储位置
- subtitle = "/Users/grtsinry43/Documents/ActivityAnalyzer", // 示例路径
+ subtitle = "/Users/grtsinry43/Documents/Chronosight", // 示例路径
icon = Icons.Default.FolderOpen, // 打开文件夹图标
colors = colors,
onClick = { /* TODO: Open file dialog or path editor */ } // 打开文件对话框或路径编辑器
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 859945e..569507e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.5.2"
+agp = "8.10.0"
android-compileSdk = "35"
android-minSdk = "24"
android-targetSdk = "35"
@@ -15,8 +15,9 @@ compose-multiplatform = "1.7.3"
junit = "4.13.2"
kotlin = "2.1.20"
kotlinx-coroutines = "1.10.1"
-kotlinxSerializationJson = "1.8.0"
-ktorClientCore = "3.1.1"
+kotlinxSerializationJson = "1.8.1"
+ktorClientCore = "3.1.3"
+mokoResources = "0.24.5"
[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
@@ -38,10 +39,14 @@ ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negoti
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientCore" }
ktor-client-ios = { module = "io.ktor:ktor-client-ios", version.ref = "ktorClientCore" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktorClientCore" }
+moko-resources = { group = "dev.icerock.moko", name = "resources", version.ref = "mokoResources" }
+moko-resources-compose = { group = "dev.icerock.moko", name = "resources-compose", version.ref = "mokoResources" }
+resources-test = { module = "dev.icerock.moko:resources-test", version.ref = "mokoResources" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
-kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
\ No newline at end of file
+kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
+mokoMultiplatformResources = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "mokoResources" }
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 09523c0..e2847c8 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/iosApp/Configuration/Config.xcconfig b/iosApp/Configuration/Config.xcconfig
index c228792..f42b47a 100644
--- a/iosApp/Configuration/Config.xcconfig
+++ b/iosApp/Configuration/Config.xcconfig
@@ -1,3 +1,3 @@
TEAM_ID=
-BUNDLE_ID=com.grtsinry43.activityanalyzer.ActivityAnalyzer
-APP_NAME=Activity Analyzer
\ No newline at end of file
+BUNDLE_ID=com.grtsinry43.chronosight.Chronosight
+APP_NAME=Chronosight
\ No newline at end of file
diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj
index cb516e9..fc8e420 100644
--- a/iosApp/iosApp.xcodeproj/project.pbxproj
+++ b/iosApp/iosApp.xcodeproj/project.pbxproj
@@ -18,7 +18,7 @@
058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; };
- 7555FF7B242A565900829871 /* Activity Analyzer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Activity Analyzer.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7555FF7B242A565900829871 /* Chronosight.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Chronosight.app"; sourceTree = BUILT_PRODUCTS_DIR; };
7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; };
@@ -64,7 +64,7 @@
7555FF7C242A565900829871 /* Products */ = {
isa = PBXGroup;
children = (
- 7555FF7B242A565900829871 /* Activity Analyzer.app */,
+ 7555FF7B242A565900829871 /* Chronosight.app */,
);
name = Products;
sourceTree = "";
@@ -110,7 +110,7 @@
packageProductDependencies = (
);
productName = iosApp;
- productReference = 7555FF7B242A565900829871 /* Activity Analyzer.app */;
+ productReference = 7555FF7B242A565900829871 /* Chronosight.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 594e53a..971653a 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,4 +1,4 @@
-rootProject.name = "ActivityAnalyzer"
+rootProject.name = "Chronosight"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
pluginManagement {
diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts
index 25f6dc7..46e2e91 100644
--- a/shared/build.gradle.kts
+++ b/shared/build.gradle.kts
@@ -1,11 +1,13 @@
+import dev.icerock.gradle.MRVisibility
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import dev.icerock.gradle.MultiplatformResourcesPluginExtension // Required for resourcesPackage configuration
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
-
kotlin("plugin.serialization") version libs.versions.kotlin.get()
+ alias(libs.plugins.mokoMultiplatformResources) // Use alias from version catalog
}
kotlin {
@@ -15,7 +17,7 @@ kotlin {
jvmTarget.set(JvmTarget.JVM_11)
}
}
-
+
listOf(
iosX64(),
iosArm64(),
@@ -26,16 +28,19 @@ kotlin {
isStatic = true
}
}
-
+
jvm()
-
+
sourceSets {
- commonMain.dependencies {
- // put your Multiplatform dependencies here
- implementation(libs.ktor.client.core)
- implementation(libs.ktor.client.content.negotiation)
- implementation(libs.ktor.serialization.kotlinx.json)
- implementation(libs.kotlinx.serialization.json)
+ val commonMain by getting {
+ resources.srcDirs("src/commonMain/resources")
+ dependencies {
+ implementation(libs.ktor.client.core)
+ implementation(libs.ktor.client.content.negotiation)
+ implementation(libs.ktor.serialization.kotlinx.json)
+ implementation(libs.kotlinx.serialization.json)
+ implementation(libs.moko.resources)
+ }
}
androidMain.dependencies {
implementation(libs.ktor.client.android)
@@ -47,7 +52,7 @@ kotlin {
}
android {
- namespace = "com.grtsinry43.activityanalyzer.shared"
+ namespace = "com.grtsinry43.chronosight.shared"
compileSdk = libs.versions.android.compileSdk.get().toInt()
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
@@ -57,3 +62,20 @@ android {
minSdk = libs.versions.android.minSdk.get().toInt()
}
}
+
+
+dependencies {
+ commonMainApi(libs.moko.resources)
+ commonMainApi(libs.moko.resources.compose) // for compose multiplatform
+
+ commonTestImplementation(libs.resources.test)
+}
+
+multiplatformResources {
+ resourcesPackage.set("com.grtsinry43.chronosight") // required
+ resourcesClassName.set("MR") // optional, default MR
+ resourcesVisibility.set(MRVisibility.Public) // optional, default Public
+ iosBaseLocalizationRegion.set("en") // optional, default "en"
+ iosMinimalDeploymentTarget.set("11.0") // optional, default "9.0"
+}
+
diff --git a/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/Platform.android.kt b/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/Platform.android.kt
deleted file mode 100644
index 27cdeef..0000000
--- a/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/Platform.android.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.grtsinry43.activityanalyzer
-
-import android.os.Build
-
-class AndroidPlatform : Platform {
- override val name: String = "Android ${Build.VERSION.SDK_INT}"
-}
-
-actual fun getPlatform(): Platform = AndroidPlatform()
\ No newline at end of file
diff --git a/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.kt b/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.kt
deleted file mode 100644
index 8321ed4..0000000
--- a/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.grtsinry43.activityanalyzer
-
-actual fun getScreenTime(): ScreenTime {
- return object : ScreenTime {
- override val screenTime: Long
- get() = 0L // TODO: Implement the actual logic to get screen time on Android
- }
-}
\ No newline at end of file
diff --git a/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt b/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
deleted file mode 100644
index 2ea8f58..0000000
--- a/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-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
- }
- }
-}
\ No newline at end of file
diff --git a/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt b/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
deleted file mode 100644
index f54a5d8..0000000
--- a/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-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"))
- }
-}
\ No newline at end of file
diff --git a/shared/src/androidMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt b/shared/src/androidMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt
new file mode 100644
index 0000000..b8a26f9
--- /dev/null
+++ b/shared/src/androidMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt
@@ -0,0 +1,53 @@
+package com.grtsinry43.chronosight
+
+import android.app.usage.UsageStatsManager
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.SharedPreferences
+import android.os.Build
+
+actual fun getAppUsageStats(startMillis: Long, endMillis: Long): List {
+ val context = appContext ?: return emptyList()
+ val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
+ val pm = context.packageManager
+ val prefs = context.getSharedPreferences("app_usage_stats", Context.MODE_PRIVATE)
+ val stats = usageStatsManager.queryUsageStats(
+ UsageStatsManager.INTERVAL_DAILY, startMillis, endMillis
+ ) ?: return loadPersistedStats(prefs)
+ val result = stats
+ .filter { it.totalTimeInForeground > 0 }
+ .map {
+ val appName = try {
+ pm.getApplicationLabel(pm.getApplicationInfo(it.packageName, 0)).toString()
+ } catch (e: Exception) {
+ it.packageName
+ }
+ AppUsageInfo(
+ packageName = it.packageName,
+ appName = appName,
+ usageMillis = if (Build.VERSION.SDK_INT >= 29) it.totalTimeVisible else it.totalTimeInForeground
+ )
+ }
+ .sortedByDescending { it.usageMillis }
+ persistStats(prefs, result)
+ return result
+}
+
+private fun persistStats(prefs: SharedPreferences, stats: List) {
+ val today = java.text.SimpleDateFormat("yyyy-MM-dd").format(java.util.Date())
+ val value = stats.joinToString("|") { "${it.packageName},${it.appName},${it.usageMillis}" }
+ prefs.edit().putString(today, value).apply()
+}
+
+private fun loadPersistedStats(prefs: SharedPreferences): List {
+ val today = java.text.SimpleDateFormat("yyyy-MM-dd").format(java.util.Date())
+ val value = prefs.getString(today, null) ?: return emptyList()
+ return value.split("|").mapNotNull {
+ val parts = it.split(",")
+ if (parts.size == 3) AppUsageInfo(parts[0], parts[1], parts[2].toLongOrNull() ?: 0L) else null
+ }
+}
+
+// 需在 Application 初始化时赋值
+var appContext: Context? = null
+
diff --git a/shared/src/androidMain/kotlin/com/grtsinry43/chronosight/Platform.android.kt b/shared/src/androidMain/kotlin/com/grtsinry43/chronosight/Platform.android.kt
new file mode 100644
index 0000000..9b897cb
--- /dev/null
+++ b/shared/src/androidMain/kotlin/com/grtsinry43/chronosight/Platform.android.kt
@@ -0,0 +1,11 @@
+package com.grtsinry43.chronosight
+
+import android.os.Build
+
+class AndroidPlatform : Platform {
+ override val name: String = "Android ${Build.VERSION.SDK_INT}"
+ override val isAndroid: Boolean = true
+}
+
+actual fun getPlatform(): Platform = AndroidPlatform()
+
diff --git a/shared/src/androidMain/kotlin/com/grtsinry43/chronosight/ScreenTime.kt b/shared/src/androidMain/kotlin/com/grtsinry43/chronosight/ScreenTime.kt
new file mode 100644
index 0000000..3169f1b
--- /dev/null
+++ b/shared/src/androidMain/kotlin/com/grtsinry43/chronosight/ScreenTime.kt
@@ -0,0 +1,116 @@
+package com.grtsinry43.chronosight
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.PowerManager
+import android.os.SystemClock
+
+class AppUsageTimerAndroid : ScreenTime {
+ private var totalUsage: Long = 0
+ private var lastResume: Long = 0
+ private var isResumed = false
+
+ fun onAppResume() {
+ if (!isResumed) {
+ lastResume = SystemClock.elapsedRealtime()
+ isResumed = true
+ }
+ }
+
+ fun onAppPause() {
+ if (isResumed) {
+ totalUsage += SystemClock.elapsedRealtime() - lastResume
+ isResumed = false
+ }
+ }
+
+ override fun getUsageMillis(): Long {
+ return if (isResumed) {
+ totalUsage + (SystemClock.elapsedRealtime() - lastResume)
+ } else {
+ totalUsage
+ }
+ }
+}
+
+class GlobalScreenTimeManager(private val context: Context) : ScreenTime {
+ private var screenOnTime: Long = 0L
+ private var lastScreenOnTimestamp: Long = 0L
+ private var isScreenOn: Boolean = false
+ private var isRegistered = false
+
+ private val screenReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ when (intent?.action) {
+ Intent.ACTION_SCREEN_ON -> {
+ lastScreenOnTimestamp = System.currentTimeMillis()
+ isScreenOn = true
+ }
+ Intent.ACTION_SCREEN_OFF -> {
+ if (isScreenOn) {
+ screenOnTime += System.currentTimeMillis() - lastScreenOnTimestamp
+ isScreenOn = false
+ }
+ }
+ }
+ }
+ }
+
+ fun startTracking() {
+ if (!isRegistered) {
+ val filter = IntentFilter().apply {
+ addAction(Intent.ACTION_SCREEN_ON)
+ addAction(Intent.ACTION_SCREEN_OFF)
+ }
+ context.registerReceiver(screenReceiver, filter)
+ val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
+ isScreenOn = pm.isInteractive
+ if (isScreenOn) {
+ lastScreenOnTimestamp = System.currentTimeMillis()
+ }
+ isRegistered = true
+ }
+ }
+
+ fun stopTracking() {
+ if (isRegistered) {
+ context.unregisterReceiver(screenReceiver)
+ if (isScreenOn) {
+ screenOnTime += System.currentTimeMillis() - lastScreenOnTimestamp
+ isScreenOn = false
+ }
+ isRegistered = false
+ }
+ }
+
+ override fun getUsageMillis(): Long {
+ var total = screenOnTime
+ if (isScreenOn) {
+ total += System.currentTimeMillis() - lastScreenOnTimestamp
+ }
+ return total
+ }
+}
+
+private var globalScreenTimeManager: GlobalScreenTimeManager? = null
+
+actual fun getScreenTime(): ScreenTime {
+ if (globalScreenTimeManager == null) {
+ // 需要在 Application 初始化时传入 context
+ throw IllegalStateException("GlobalScreenTimeManager not initialized. Call initGlobalScreenTimeManager(context) in Application.onCreate().")
+ }
+ return globalScreenTimeManager!!
+}
+
+fun initGlobalScreenTimeManager(context: Context) {
+ if (globalScreenTimeManager == null) {
+ globalScreenTimeManager = GlobalScreenTimeManager(context.applicationContext)
+ globalScreenTimeManager?.startTracking()
+ }
+}
+
+fun stopGlobalScreenTimeManager() {
+ globalScreenTimeManager?.stopTracking()
+}
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/Platform.kt b/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/Platform.kt
deleted file mode 100644
index 9d2d616..0000000
--- a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/Platform.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.grtsinry43.activityanalyzer
-
-interface Platform {
- val name: String
-}
-
-expect fun getPlatform(): Platform
\ No newline at end of file
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt b/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
deleted file mode 100644
index 9bd4d3e..0000000
--- a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.grtsinry43.activityanalyzer.i18n
-
-expect class StringResource {
- fun getString(key: String): String
- fun getString(key: String, vararg args: Any): String
-}
\ No newline at end of file
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt b/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
deleted file mode 100644
index 694a937..0000000
--- a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.grtsinry43.activityanalyzer.i18n
-
-expect object StringResourceFactory {
- fun create(): StringResource
-}
\ No newline at end of file
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt
new file mode 100644
index 0000000..8e728b8
--- /dev/null
+++ b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt
@@ -0,0 +1,18 @@
+package com.grtsinry43.chronosight
+
+/**
+ * 单个应用的使用时长统计
+ */
+data class AppUsageInfo(
+ val packageName: String, // Android: 包名,Linux: 进程名
+ val appName: String, // 应用名
+ val usageMillis: Long // 使用时长(毫秒)
+)
+
+/**
+ * 获取一段时间内各应用的使用时长统计
+ * @param startMillis 开始时间(时间戳,毫秒)
+ * @param endMillis 结束时间(时间戳,毫秒)
+ */
+expect fun getAppUsageStats(startMillis: Long, endMillis: Long): List
+
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/Greeting.kt b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/Greeting.kt
similarity index 94%
rename from shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/Greeting.kt
rename to shared/src/commonMain/kotlin/com/grtsinry43/chronosight/Greeting.kt
index c3963a5..2e2a004 100644
--- a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/Greeting.kt
+++ b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/Greeting.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer
+package com.grtsinry43.chronosight
import io.ktor.client.call.body
import io.ktor.client.request.get
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/NetworkModule.kt b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/NetworkModule.kt
similarity index 93%
rename from shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/NetworkModule.kt
rename to shared/src/commonMain/kotlin/com/grtsinry43/chronosight/NetworkModule.kt
index f0a64f9..c14245f 100644
--- a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/NetworkModule.kt
+++ b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/NetworkModule.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer
+package com.grtsinry43.chronosight
import io.ktor.client.HttpClient
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/Platform.kt b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/Platform.kt
new file mode 100644
index 0000000..192083a
--- /dev/null
+++ b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/Platform.kt
@@ -0,0 +1,20 @@
+package com.grtsinry43.chronosight
+
+interface Platform {
+ val name: String
+ val isAndroid: Boolean
+
+ companion object {
+ val current: Platform by lazy { getPlatform() }
+
+ // Convenience properties to access platform information
+ val isAndroid: Boolean
+ get() = current.isAndroid
+
+ val name: String
+ get() = current.name
+ }
+}
+
+expect fun getPlatform(): Platform
+
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.kt b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/ScreenTime.kt
similarity index 53%
rename from shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.kt
rename to shared/src/commonMain/kotlin/com/grtsinry43/chronosight/ScreenTime.kt
index 002ee74..cb366ba 100644
--- a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.kt
+++ b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/ScreenTime.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer
+package com.grtsinry43.chronosight
/**
* @author grtsinry43
@@ -6,7 +6,8 @@ package com.grtsinry43.activityanalyzer
* @description 热爱可抵岁月漫长
*/
interface ScreenTime {
- val screenTime: Long
+ fun getUsageMillis(): Long
}
-expect fun getScreenTime(): ScreenTime
\ No newline at end of file
+expect fun getScreenTime(): ScreenTime
+
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/ScreenTimeUtil.kt b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/ScreenTimeUtil.kt
new file mode 100644
index 0000000..a2b5e78
--- /dev/null
+++ b/shared/src/commonMain/kotlin/com/grtsinry43/chronosight/ScreenTimeUtil.kt
@@ -0,0 +1,12 @@
+package com.grtsinry43.chronosight
+
+/**
+ * 工具函数:毫秒转xh ym
+ */
+fun formatMillisToHourMin(millis: Long): String {
+ val totalMinutes = millis / 60000
+ val hours = totalMinutes / 60
+ val minutes = totalMinutes % 60
+ return "${hours}h ${minutes}m"
+}
+
diff --git a/shared/src/commonMain/moko-resources/base/strings.xml b/shared/src/commonMain/moko-resources/base/strings.xml
new file mode 100644
index 0000000..a3e2a0e
--- /dev/null
+++ b/shared/src/commonMain/moko-resources/base/strings.xml
@@ -0,0 +1,42 @@
+
+
+ Chronosight
+ Welcome Back, %s!
+ Today's Screen Time
+ You're on track with your daily goal!
+ Quick Glance: Top Apps
+ Go to grant Usage Access
+ Quick Actions
+ Start Focus Session
+ View Today's Details
+ Recent Insights
+ Exceeded daily goal for App X.
+ Screen time 20%% higher yesterday.
+ Home
+ Analytics
+ Reports
+ Profile
+ Settings
+ About
+ grtsinry43@outlook.com
+ Last 7 Days
+ Today
+ Yesterday
+ Last 30 Days
+ Screen Time Analytics
+ Total Screen Time
+ Avg Daily Time
+ Most Used App
+ Pickups
+ Usage Patterns
+ Daily Screen Time (Bar Chart)
+ App Usage (Pie Chart)
+ App Usage Breakdown
+ No usage data.
+ Analysis Tools
+ App Breakdown
+ Time of Day
+ Usage Goals
+ View Today's Details
+
+
diff --git a/shared/src/commonMain/moko-resources/zh/strings.xml b/shared/src/commonMain/moko-resources/zh/strings.xml
new file mode 100644
index 0000000..d7388bf
--- /dev/null
+++ b/shared/src/commonMain/moko-resources/zh/strings.xml
@@ -0,0 +1,42 @@
+
+
+ 活动分析器
+ 欢迎回来,%s!
+ 今日屏幕时间
+ 你已接近今日目标!
+ 速览:高频应用
+ 前往授权使用情况访问
+ 快捷操作
+ 开始专注模式
+ 查看今日详情
+ 近期洞察
+ App X 超出每日目标。
+ 昨日屏幕时间高出 20%。
+ 屏幕时间分析
+ 总亮屏时长
+ 日均时长
+ 最常用应用
+ 点亮次数
+ 使用模式
+ 每日屏幕时间(柱状图)
+ 应用使用(饼图)
+ 应用使用明细
+ 无使用数据或未授权。
+ 分析工具
+ 应用明细
+ 时段分析
+ 设定目标
+ 查看今日详情
+ 首页
+ 分析
+ 报表
+ 我的
+ 设置
+ 关于
+ grtsinry43@outlook.com
+ 最近7天
+ 今天
+ 昨天
+ 最近30天
+
+
diff --git a/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt b/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
deleted file mode 100644
index 74eb1d4..0000000
--- a/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-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)
- }
-}
\ No newline at end of file
diff --git a/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt b/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
deleted file mode 100644
index 473f7d4..0000000
--- a/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-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)
- }
-}
\ No newline at end of file
diff --git a/shared/src/desktopMain/resources/strings.properties b/shared/src/desktopMain/resources/strings.properties
index 5b5542f..305dfce 100644
--- a/shared/src/desktopMain/resources/strings.properties
+++ b/shared/src/desktopMain/resources/strings.properties
@@ -1,5 +1,5 @@
-app.title=Activity Analyzer
-home.welcome=Welcome to Activity Analyzer
+app.title=Chronosight
+home.welcome=Welcome to Chronosight
home.quickActions=Quick Actions
home.newAnalysis=New Analysis
home.importData=Import Data
@@ -16,7 +16,7 @@ settings.title=Settings
settings.dataStorage=Data Storage Location
settings.autoBackup=Auto Backup
settings.notifications=Notifications
-about.title=About Activity Analyzer
+about.title=About Chronosight
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
\ No newline at end of file
diff --git a/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt b/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
deleted file mode 100644
index c40baf6..0000000
--- a/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-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])
- }
- }
- }
-}
\ No newline at end of file
diff --git a/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt b/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
deleted file mode 100644
index ff355e5..0000000
--- a/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.grtsinry43.activityanalyzer.i18n
-
-actual object StringResourceFactory {
- actual fun create(): StringResource {
- return StringResource()
- }
-}
\ No newline at end of file
diff --git a/shared/src/iosMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt b/shared/src/iosMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt
new file mode 100644
index 0000000..c24634d
--- /dev/null
+++ b/shared/src/iosMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt
@@ -0,0 +1,4 @@
+package com.grtsinry43.chronosight
+
+actual fun getAppUsageStats(startMillis: Long, endMillis: Long): List = emptyList()
+
diff --git a/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/Platform.ios.kt b/shared/src/iosMain/kotlin/com/grtsinry43/chronosight/Platform.ios.kt
similarity index 57%
rename from shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/Platform.ios.kt
rename to shared/src/iosMain/kotlin/com/grtsinry43/chronosight/Platform.ios.kt
index 72a742a..9f8e684 100644
--- a/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/Platform.ios.kt
+++ b/shared/src/iosMain/kotlin/com/grtsinry43/chronosight/Platform.ios.kt
@@ -1,9 +1,11 @@
-package com.grtsinry43.activityanalyzer
+package com.grtsinry43.chronosight
import platform.UIKit.UIDevice
class IOSPlatform: Platform {
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
+ override val isAndroid: Boolean = false
}
-actual fun getPlatform(): Platform = IOSPlatform()
\ No newline at end of file
+actual fun getPlatform(): Platform = IOSPlatform()
+
diff --git a/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/Platform.jvm.kt b/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/Platform.jvm.kt
deleted file mode 100644
index df34fc4..0000000
--- a/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/Platform.jvm.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.grtsinry43.activityanalyzer
-
-class JVMPlatform: Platform {
- override val name: String = "Java ${System.getProperty("java.version")}"
-}
-
-actual fun getPlatform(): Platform = JVMPlatform()
\ No newline at end of file
diff --git a/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.kt b/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.kt
deleted file mode 100644
index 5f6fa14..0000000
--- a/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.grtsinry43.activityanalyzer
-
-import java.io.BufferedReader
-import java.io.InputStreamReader
-
-/**
- * @author grtsinry43
- * @date 2025/4/14 16:30
- * @description 热爱可抵岁月漫长
- */
-class LinuxScreenTime : ScreenTime {
- override val screenTime: Long
- get() {
- try {
- println("===")
- // Execute qdbus command to query active window information
- val process = Runtime.getRuntime().exec("qdbus org.kde.KWin /KWin queryWindowInfo")
- val reader = BufferedReader(InputStreamReader(process.inputStream))
- val output = reader.readLines()
- reader.close()
-
- // Parse the output to find the caption (window title)
- val captionLine = output.find { it.trim().startsWith("caption:") }
- val windowTitle = captionLine?.substringAfter("caption:")?.trim()
-
- if (windowTitle != null) {
- println("Active Window Title: $windowTitle")
- return 3600 // Example: Return dummy screen time
- }
- } catch (e: Exception) {
- e.printStackTrace()
- }
- println("Failed to get active window title.")
- return 0
- }
-}
-
-actual fun getScreenTime(): ScreenTime = LinuxScreenTime()
\ No newline at end of file
diff --git a/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt b/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
deleted file mode 100644
index 74eb1d4..0000000
--- a/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-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)
- }
-}
\ No newline at end of file
diff --git a/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt b/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
deleted file mode 100644
index 473f7d4..0000000
--- a/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-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)
- }
-}
\ No newline at end of file
diff --git a/shared/src/jvmMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt b/shared/src/jvmMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt
new file mode 100644
index 0000000..f901c6e
--- /dev/null
+++ b/shared/src/jvmMain/kotlin/com/grtsinry43/chronosight/AppUsageAnalyzer.kt
@@ -0,0 +1,76 @@
+package com.grtsinry43.chronosight
+
+import java.io.File
+import java.time.LocalDate
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+import java.util.concurrent.ConcurrentHashMap
+
+class LinuxAppUsageAnalyzer {
+ private val usageMap = ConcurrentHashMap() // 进程名 -> 累计时长
+ private var lastActiveProcess: String? = null
+ private var lastActiveTimestamp: Long = System.currentTimeMillis()
+ private val statsFile = File(System.getProperty("user.home"), ".activity_analyzer_app_usage")
+ private val today: String = LocalDate.now().format(DateTimeFormatter.ISO_DATE)
+
+ init {
+ // 启动后台线程,定时检测活跃窗口进程名
+ Thread {
+ while (true) {
+ val proc = getActiveProcessName()
+ val now = System.currentTimeMillis()
+ if (lastActiveProcess != null) {
+ val delta = now - lastActiveTimestamp
+ usageMap[lastActiveProcess!!] = (usageMap[lastActiveProcess!!] ?: 0L) + delta
+ }
+ lastActiveProcess = proc
+ lastActiveTimestamp = now
+ Thread.sleep(5000)
+ saveTodayStats()
+ }
+ }.apply { isDaemon = true }.start()
+ }
+
+ private fun getActiveProcessName(): String {
+ // 依赖 wmctrl 工具
+ return try {
+ val winId = Runtime.getRuntime().exec(arrayOf("bash", "-c", "xprop -root _NET_ACTIVE_WINDOW | awk '{print $5}'")).inputStream.bufferedReader().readText().trim().removePrefix("0x").ifEmpty { return "unknown" }
+ val pidLine = Runtime.getRuntime().exec(arrayOf("bash", "-c", "xprop -id 0x$winId _NET_WM_PID")).inputStream.bufferedReader().readText()
+ val pid = Regex("_NET_WM_PID\\(CARDINAL\\) = (\\d+)").find(pidLine)?.groupValues?.getOrNull(1) ?: return "unknown"
+ val procName = Runtime.getRuntime().exec(arrayOf("bash", "-c", "ps -p $pid -o comm= ")).inputStream.bufferedReader().readText().trim()
+ if (procName.isNotEmpty()) procName else "unknown"
+ } catch (e: Exception) {
+ "unknown"
+ }
+ }
+
+ private fun saveTodayStats() {
+ val lines = usageMap.entries.joinToString("\n") { "${today}:${it.key}:${it.value}" }
+ statsFile.writeText(lines)
+ }
+
+ private fun loadTodayStats() {
+ if (!statsFile.exists()) return
+ statsFile.readLines().forEach { line ->
+ val parts = line.split(":")
+ if (parts.size == 3 && parts[0] == today) {
+ usageMap[parts[1]] = parts[2].toLongOrNull() ?: 0L
+ }
+ }
+ }
+
+ fun getStats(): List {
+ loadTodayStats()
+ return usageMap.map { (proc, ms) ->
+ AppUsageInfo(
+ packageName = proc,
+ appName = proc,
+ usageMillis = ms
+ )
+ }.sortedByDescending { it.usageMillis }
+ }
+}
+
+private val analyzer by lazy { LinuxAppUsageAnalyzer() }
+
+actual fun getAppUsageStats(startMillis: Long, endMillis: Long): List = analyzer.getStats()
diff --git a/shared/src/jvmMain/kotlin/com/grtsinry43/chronosight/Platform.jvm.kt b/shared/src/jvmMain/kotlin/com/grtsinry43/chronosight/Platform.jvm.kt
new file mode 100644
index 0000000..450f656
--- /dev/null
+++ b/shared/src/jvmMain/kotlin/com/grtsinry43/chronosight/Platform.jvm.kt
@@ -0,0 +1,9 @@
+package com.grtsinry43.chronosight
+
+class JVMPlatform: Platform {
+ override val name: String = "Java ${System.getProperty("java.version")}"
+ override val isAndroid: Boolean = false
+}
+
+actual fun getPlatform(): Platform = JVMPlatform()
+
diff --git a/shared/src/jvmMain/kotlin/com/grtsinry43/chronosight/ScreenTime.kt b/shared/src/jvmMain/kotlin/com/grtsinry43/chronosight/ScreenTime.kt
new file mode 100644
index 0000000..8b995dd
--- /dev/null
+++ b/shared/src/jvmMain/kotlin/com/grtsinry43/chronosight/ScreenTime.kt
@@ -0,0 +1,56 @@
+package com.grtsinry43.chronosight
+
+import java.io.File
+import java.time.LocalDate
+import java.time.format.DateTimeFormatter
+
+/**
+ * Linux 桌面端屏幕时长统计(本地累计,按天存储)
+ */
+class LinuxScreenTime : ScreenTime {
+ private val statsFile = File(System.getProperty("user.home"), ".activity_analyzer_screen_time")
+ private val today: String = LocalDate.now().format(DateTimeFormatter.ISO_DATE)
+
+ // 读取今日累计时长(毫秒)
+ private fun readTodayMillis(): Long {
+ if (!statsFile.exists()) return 0L
+ val line = statsFile.readLines().find { it.startsWith("") || it.startsWith(today) }
+ return line?.split(":")?.getOrNull(1)?.toLongOrNull() ?: 0L
+ }
+
+ // 写入今日累计时长
+ private fun writeTodayMillis(millis: Long) {
+ val lines = if (statsFile.exists()) statsFile.readLines().toMutableList() else mutableListOf()
+ val idx = lines.indexOfFirst { it.startsWith(today) }
+ if (idx >= 0) {
+ lines[idx] = "$today:$millis"
+ } else {
+ lines.add("$today:$millis")
+ }
+ statsFile.writeText(lines.joinToString("\n"))
+ }
+
+ private var lastActive: Long = System.currentTimeMillis()
+ private var cachedMillis: Long = readTodayMillis()
+
+ init {
+ // 启动定时器,每5秒累计一次活跃时长
+ Thread {
+ while (true) {
+ Thread.sleep(5000)
+ val now = System.currentTimeMillis()
+ cachedMillis += now - lastActive
+ writeTodayMillis(cachedMillis)
+ lastActive = now
+ }
+ }.apply { isDaemon = true }.start()
+ }
+
+ override fun getUsageMillis(): Long {
+ // 返回内存中的今日累计时长
+ return cachedMillis + (System.currentTimeMillis() - lastActive)
+ }
+}
+
+actual fun getScreenTime(): ScreenTime = LinuxScreenTime()
+
diff --git a/shared/src/jvmMain/resources/strings.properties b/shared/src/jvmMain/resources/strings.properties
index 5b5542f..305dfce 100644
--- a/shared/src/jvmMain/resources/strings.properties
+++ b/shared/src/jvmMain/resources/strings.properties
@@ -1,5 +1,5 @@
-app.title=Activity Analyzer
-home.welcome=Welcome to Activity Analyzer
+app.title=Chronosight
+home.welcome=Welcome to Chronosight
home.quickActions=Quick Actions
home.newAnalysis=New Analysis
home.importData=Import Data
@@ -16,7 +16,7 @@ settings.title=Settings
settings.dataStorage=Data Storage Location
settings.autoBackup=Auto Backup
settings.notifications=Notifications
-about.title=About Activity Analyzer
+about.title=About Chronosight
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
\ No newline at end of file
diff --git a/shared/src/nativeMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.native.kt b/shared/src/nativeMain/kotlin/com/grtsinry43/chronosight/ScreenTime.native.kt
similarity index 65%
rename from shared/src/nativeMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.native.kt
rename to shared/src/nativeMain/kotlin/com/grtsinry43/chronosight/ScreenTime.native.kt
index 7b1892e..a80afde 100644
--- a/shared/src/nativeMain/kotlin/com/grtsinry43/activityanalyzer/ScreenTime.native.kt
+++ b/shared/src/nativeMain/kotlin/com/grtsinry43/chronosight/ScreenTime.native.kt
@@ -1,4 +1,4 @@
-package com.grtsinry43.activityanalyzer
+package com.grtsinry43.chronosight
actual fun getScreenTime(): ScreenTime {
TODO("Not yet implemented")