From dad89bcc8104c5c999f1f7fe3611e01f876a603c Mon Sep 17 00:00:00 2001 From: grtsinry43 Date: Mon, 12 May 2025 09:14:19 +0800 Subject: [PATCH] Dev (#1) --- .github/workflows/build.yml | 179 ++++- .../activityanalyzer/MainActivity.kt | 3 + .../com/grtsinry43/activityanalyzer/App.kt | 204 ++++- .../components/ActivityItemC.kt | 81 ++ .../components/ChartPlaceholderC.kt | 60 ++ .../activityanalyzer/components/InfoItemC.kt | 95 +++ .../components/MetricCardC.kt | 80 ++ .../components/ReportItemC.kt | 98 +++ .../components/SettingItemC.kt | 136 ++++ .../components/SimpleListItemC.kt | 71 ++ .../components/StyledButtonC.kt | 72 ++ .../activityanalyzer/components/StyledCard.kt | 45 ++ .../screens/MobileAboutScreenC.kt | 165 ++++ .../screens/MobileAnalyticsScreenC.kt | 191 +++++ .../screens/MobileHomeScreenC.kt | 174 +++++ .../screens/MobileProfileScreenC.kt | 188 +++++ .../screens/MobileReportsScreenC.kt | 104 +++ .../screens/MobileSettingsScreenC.kt | 182 +++++ .../activityanalyzer/theme/AppThemes.kt | 43 ++ .../grtsinry43/activityanalyzer/DesktopApp.kt | 704 +++--------------- .../activityanalyzer/components/NavItemC.kt | 132 ++++ .../com/grtsinry43/activityanalyzer/main.kt | 121 ++- .../activityanalyzer/screens/AboutScreenC.kt | 173 +++++ .../screens/AnalyticsScreenC.kt | 222 ++++++ .../activityanalyzer/screens/HomeScreenC.kt | 184 +++++ .../screens/ProfileScreenC.kt | 183 +++++ .../screens/ReportsScreenC.kt | 123 +++ .../screens/SettingsScreenC.kt | 207 +++++ iosApp/iosApp/BottomBar.swift | 95 ++- 29 files changed, 3627 insertions(+), 688 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ActivityItemC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ChartPlaceholderC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/InfoItemC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/MetricCardC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ReportItemC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SettingItemC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SimpleListItemC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledButtonC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledCard.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAboutScreenC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAnalyticsScreenC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileHomeScreenC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileProfileScreenC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileReportsScreenC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileSettingsScreenC.kt create mode 100644 composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/theme/AppThemes.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/components/NavItemC.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AboutScreenC.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AnalyticsScreenC.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/HomeScreenC.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ProfileScreenC.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ReportsScreenC.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/SettingsScreenC.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61e6e26..f6edf1c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,46 +1,159 @@ -name: Build and Release +name: KMP Build & Package on: push: + branches: [ main, dev ] # 在推送到 main 或 develop 分支时触发 tags: - - 'v*.*.*' # 仅在推送符合语义化版本的标签时触发 + - 'v*' # 在推送版本标签时触发 (例如 v1.0.0) + pull_request: + branches: [ main, dev ] # 在向 main 或 develop 分支发起 Pull Request 时触发 + workflow_dispatch: # 允许手动触发 jobs: build: - name: Build All Targets - runs-on: ubuntu-latest + strategy: + fail-fast: false # 即使一个 job 失败,其他 job 也会继续运行 + matrix: + os: [ ubuntu-latest, macos-latest, windows-latest ] # 根据你的目标平台选择操作系统 + include: + # Android 构建 (通常在 Linux 环境下进行) + - os: ubuntu-latest + target: android + description: "Build Android App" + gradle_task: ":shared:assembleRelease :androidApp:assembleRelease" # 替换成你的 Android 打包任务 + artifact_name: "android-app" + artifact_path: | + shared/build/outputs/aar/*.aar + androidApp/build/outputs/apk/release/*.apk + androidApp/build/outputs/bundle/release/*.aab + # iOS 构建 (必须在 macOS 环境下进行) + - os: macos-latest + target: ios + description: "Build iOS Framework" + # 注意: iOS 构建可能需要特定的 Xcode 版本,可以使用 actions/setup-xcode + # gradle_task: ":shared:packForXCFramework" # 常见的 iOS framework 打包任务 + gradle_task: ":shared:assembleXCFramework" # 或者使用新的 Kotlin 插件的任务名 + artifact_name: "ios-framework" + artifact_path: "shared/build/XCFrameworks/release/*.xcframework" # 检查你的实际输出路径 + # JVM/Desktop 构建 (可以在多个操作系统上运行) + - os: ubuntu-latest # 为 Linux 构建 + target: jvm_linux + description: "Build JVM/Desktop (Linux)" + gradle_task: ":desktopApp:packageDistributionForCurrentOS" # 替换成你的 Desktop 打包任务 + artifact_name: "desktop-app-linux" + artifact_path: "desktopApp/build/compose/binaries/main/app/*" # 检查你的实际输出路径 + - os: macos-latest # 为 macOS 构建 + target: jvm_macos + description: "Build JVM/Desktop (macOS)" + gradle_task: ":desktopApp:packageDistributionForCurrentOS" + artifact_name: "desktop-app-macos" + artifact_path: "desktopApp/build/compose/binaries/main/app/*" # 检查你的实际输出路径 + - os: windows-latest # 为 Windows 构建 + target: jvm_windows + description: "Build JVM/Desktop (Windows)" + gradle_task: ":desktopApp:packageDistributionForCurrentOS" + artifact_name: "desktop-app-windows" + artifact_path: "desktopApp/build/compose/binaries/main/app/*" # 检查你的实际输出路径 + # JavaScript 构建 (通常在 Linux 环境下进行) + - os: ubuntu-latest + target: js + description: "Build JavaScript" + gradle_task: ":shared:jsBrowserDistribution" # 替换成你的 JS 打包任务 + artifact_name: "js-app" + artifact_path: "shared/build/distributions/*" # 检查你的实际输出路径 + # Linux Native 构建 + - os: ubuntu-latest + target: linux_native + description: "Build Linux Native" + gradle_task: ":shared:linkReleaseExecutableLinuxX64" # 替换成你的 Linux Native 编译任务 + artifact_name: "linux-native-executable" + artifact_path: "shared/build/bin/linuxX64/releaseExecutable/*.kexe" # 检查你的实际输出路径 + # macOS Native 构建 (也可以针对 arm64) + - os: macos-latest + target: macos_native + description: "Build macOS Native" + gradle_task: ":shared:linkReleaseExecutableMacosX64" # 或者 linkReleaseExecutableMacosArm64 + artifact_name: "macos-native-executable" + artifact_path: "shared/build/bin/macosX64/releaseExecutable/*.kexe" # 检查你的实际输出路径 + # Windows Native 构建 + - os: windows-latest + target: windows_native + description: "Build Windows Native" + gradle_task: ":shared:linkReleaseExecutableMingwX64" # 替换成你的 Windows Native 编译任务 + artifact_name: "windows-native-executable" + artifact_path: "shared/build/bin/mingwX64/releaseExecutable/*.exe" # 检查你的实际输出路径 + + # 如果某些平台不需要在所有操作系统上构建,可以在这里排除 + # exclude: + # - os: windows-latest + # target: android + # - os: ubuntu-latest + # target: ios + + name: ${{ matrix.description }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} steps: - # 检出代码 - - name: Checkout code - uses: actions/checkout@v3 - - # 设置 JDK 环境 - - name: Set up JDK - uses: actions/setup-java@v3 + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK 17 # KMP 通常建议使用较新的 JDK 版本 + uses: actions/setup-java@v4 with: - distribution: 'zulu' + distribution: 'temurin' # 或者 'zulu', 'adopt' 等 java-version: '17' - - # 构建 Android APK - - name: Build Android APK - run: ./gradlew :androidApp:assembleRelease - - # 构建 iOS Framework - - name: Build iOS Framework - run: ./gradlew :iosApp:build - - # 构建 Desktop 可执行文件 - - name: Build Desktop Executable - run: ./gradlew :desktopApp:packageRelease - - # 上传构建产物到 GitHub Releases - - name: Upload Release Assets - uses: actions/upload-release-asset@v2 + + # 仅在 macOS runner 上为 iOS 构建设置 Xcode 版本 (如果需要特定版本) + - name: Select Xcode version (for iOS builds) + if: matrix.os == 'macos-latest' && matrix.target == 'ios' + run: sudo xcode-select -s /Applications/Xcode_15.3.app/Contents/Developer # 替换成你需要的 Xcode 版本路径 + # 你也可以使用 action 如 maxim-lobanov/setup-xcode@v1 + + # 设置 Gradle (会自动处理 Gradle Wrapper) + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 # 使用 v3 版本 + with: + gradle-version: wrapper # 默认使用 Gradle Wrapper + # cache-read-only: ${{ github.ref != 'refs/heads/main' }} # 非 main 分支构建时,缓存只读 (可选) + # cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} # 加密缓存 (可选) + + # Gradle 缓存 (KMP 项目的 .konan 目录和 Gradle 缓存很重要) + - name: Cache Gradle packages + uses: actions/cache@v4 with: - name: ${{github.ref_name}}-build path: | - androidApp/build/outputs/apk/release/*.apk - iosApp/build/bin/ios/*.framework - desktopApp/build/outputs/*.exe - label: "Build for ${{ github.ref_name }}" + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Cache Kotlin Native ($HOME/.konan) + if: matrix.os == 'macos-latest' || matrix.os == 'ubuntu-latest' # .konan 缓存主要用于 Native 编译 + uses: actions/cache@v4 + with: + path: ~/.konan + key: ${{ runner.os }}-konan-${{ hashFiles('**/build.gradle.kts', '**/gradle.properties') }} # 或者更精确的 hash + restore-keys: | + ${{ runner.os }}-konan- + + # 授予 Gradle Wrapper 执行权限 (特别是对于 Windows) + - name: Make gradlew executable + if: runner.os != 'windows-latest' + run: chmod +x ./gradlew + - name: Make gradlew.bat executable (Windows) + if: runner.os == 'windows-latest' + run: cmd /c "chmod +x gradlew.bat" # 在 Windows 上,通常 gradlew.bat 默认就有执行权限,此步骤可能非必需 + + # 执行构建任务 + - name: Build with Gradle + run: ./gradlew ${{ matrix.gradle_task }} --no-daemon # --no-daemon 可以在 CI 环境中更稳定 + + # 上传构建产物 + # 注意: actions/upload-artifact@v4 的用法与 v3 不同 + - name: Upload Artifact - ${{ matrix.artifact_name }} + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact_name }} + path: ${{ matrix.artifact_path }} + if-no-files-found: error # 如果没有找到文件则报错 \ 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 index 6492e11..6926e4b 100644 --- a/composeApp/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/MainActivity.kt @@ -3,10 +3,12 @@ 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) @@ -16,6 +18,7 @@ class MainActivity : ComponentActivity() { } } +@OptIn(ExperimentalMaterial3Api::class) @Preview @Composable fun AppAndroidPreview() { diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt index 951cec4..97f6c2d 100644 --- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt @@ -1,44 +1,192 @@ package com.grtsinry43.activityanalyzer -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.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 org.jetbrains.compose.resources.painterResource -import org.jetbrains.compose.ui.tooling.preview.Preview - -import activityanalyzer.composeapp.generated.resources.Res -import activityanalyzer.composeapp.generated.resources.compose_multiplatform +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 -@Preview +@ExperimentalMaterial3Api fun App() { - MaterialTheme { - var showContent by remember { mutableStateOf(false) } - var greetingText by remember { mutableStateOf("加载中...") } + var isDarkTheme by remember { mutableStateOf(false) } + val currentColors = if (isDarkTheme) AppThemes.DarkThemeColors else AppThemes.LightThemeColors - LaunchedEffect(key1 = Unit) { - greetingText = try { - Greeting().greet() - } catch (e: Exception) { - "错误:${e.message}" - } - } + // Navigation state + var selectedNavItemIndex by remember { mutableStateOf(0) } + val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) + val scope = rememberCoroutineScope() - Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { - Button(onClick = { showContent = !showContent }) { - Text("Click me!") + 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) + ) + ) + } + } } - AnimatedVisibility(showContent) { - Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { - Image(painterResource(Res.drawable.compose_multiplatform), null) - Text("Compose: $greetingText") + ) { + 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) + } } } } } -} \ No newline at end of file +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ActivityItemC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ActivityItemC.kt new file mode 100644 index 0000000..78aef5e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ActivityItemC.kt @@ -0,0 +1,81 @@ +package com.grtsinry43.activityanalyzer.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Info +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun ActivityItem(title: String, time: String, icon: ImageVector, colors: AppThemes.Colors) { + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(10.dp)) + .background(colors.surface.copy(alpha = 0.5f)) + .border(1.dp, colors.border.copy(alpha = 0.3f), RoundedCornerShape(10.dp)) + .padding(horizontal = 16.dp, vertical = 12.dp), // Adjusted padding for mobile + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = colors.accent, + modifier = Modifier.size(24.dp) // Slightly larger icon for mobile list items + ) + Column(modifier = Modifier.weight(1f)) { + Text( + text = title, + fontSize = 15.sp, + color = colors.onSurface, + fontWeight = FontWeight.Normal // Adjusted weight + ) + Text( + text = time, + fontSize = 13.sp, + color = colors.secondaryText + ) + } + } +} + +@Preview +@Composable +fun ActivityItemPreview() { + MaterialTheme { + ActivityItem( + title = "Sample Activity", + time = "10:00 AM", + icon = Icons.Default.Info, + colors = AppThemes.LightThemeColors + ) + } +} + +@Preview +@Composable +fun ActivityItemDarkPreview() { + MaterialTheme { + ActivityItem( + title = "Sample Activity (Dark)", + time = "10:00 AM", + icon = Icons.Default.Info, + colors = AppThemes.DarkThemeColors + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ChartPlaceholderC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ChartPlaceholderC.kt new file mode 100644 index 0000000..29cf698 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ChartPlaceholderC.kt @@ -0,0 +1,60 @@ +package com.grtsinry43.activityanalyzer.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.grtsinry43.activityanalyzer.theme.AppThemes +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun ChartPlaceholder(text: String, colors: AppThemes.Colors, modifier: Modifier = Modifier) { + Box( + modifier = modifier + .background(colors.background.copy(alpha = 0.5f), RoundedCornerShape(10.dp)) + .border(1.dp, colors.border.copy(alpha = 0.7f), RoundedCornerShape(10.dp)) + .padding(16.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = text, + color = colors.secondaryText, + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center + ) + } +} + +@Preview +@Composable +fun ChartPlaceholderPreview() { + MaterialTheme { + ChartPlaceholder( + text = "Sample Chart Placeholder", + colors = AppThemes.LightThemeColors, + modifier = Modifier.fillMaxWidth().height(200.dp) + ) + } +} + +@Preview +@Composable +fun ChartPlaceholderDarkPreview() { + MaterialTheme { + ChartPlaceholder( + text = "Sample Chart Placeholder (Dark)", + colors = AppThemes.DarkThemeColors, + modifier = Modifier.fillMaxWidth().height(200.dp) + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/InfoItemC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/InfoItemC.kt new file mode 100644 index 0000000..1999093 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/InfoItemC.kt @@ -0,0 +1,95 @@ +package com.grtsinry43.activityanalyzer.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Info +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun InfoItem( + icon: ImageVector, + text: String, + colors: AppThemes.Colors, + isLink: Boolean = false, + linkUrl: String? = null, // Not used in preview, but kept for component signature + onClick: (() -> Unit)? = null +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(8.dp)) + .clickable(enabled = onClick != null || (isLink && linkUrl != null), onClick = { + onClick?.invoke() + // if (isLink && linkUrl != null) { /* uriHandler.openUri(linkUrl) */ } // Link opening logic removed for preview + }) + .padding(vertical = 12.dp, horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = colors.accentVariant, + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(16.dp)) + Text( + text = text, + fontSize = 15.sp, + color = if (isLink) colors.accent else colors.onSurface, + fontWeight = FontWeight.Normal + ) + } +} + +@Preview +@Composable +fun InfoItemPreview() { + MaterialTheme { + InfoItem( + icon = Icons.Default.Info, + text = "This is an informational item.", + colors = AppThemes.LightThemeColors + ) + } +} + +@Preview +@Composable +fun InfoItemLinkPreview() { + MaterialTheme { + InfoItem( + icon = Icons.Default.Info, + text = "This is a clickable link item.", + colors = AppThemes.LightThemeColors, + isLink = true, + linkUrl = "https://example.com", + onClick = {} + ) + } +} + +@Preview +@Composable +fun InfoItemDarkPreview() { + MaterialTheme { + InfoItem( + icon = Icons.Default.Info, + text = "This is an informational item (Dark).", + colors = AppThemes.DarkThemeColors + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/MetricCardC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/MetricCardC.kt new file mode 100644 index 0000000..aace9a2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/MetricCardC.kt @@ -0,0 +1,80 @@ +package com.grtsinry43.activityanalyzer.components + +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Star +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun MetricCard( + title: String, + value: String, + icon: ImageVector, + colors: AppThemes.Colors, + modifier: Modifier = Modifier +) { + StyledCard(colors = colors, modifier = modifier) { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(6.dp) // Tighter spacing for mobile + ) { + Icon( + imageVector = icon, + contentDescription = title, + tint = colors.accent, + modifier = Modifier.size(28.dp) + ) + Text( + text = title, + style = MaterialTheme.typography.labelMedium.copy(color = colors.secondaryText), + textAlign = TextAlign.Center + ) + Text( + text = value, + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.Bold + ), + textAlign = TextAlign.Center + ) + } + } +} + +@Preview +@Composable +fun MetricCardPreview() { + MaterialTheme { + MetricCard( + title = "Total Users", + value = "1,234", + icon = Icons.Default.Star, + colors = AppThemes.LightThemeColors + ) + } +} + +@Preview +@Composable +fun MetricCardDarkPreview() { + MaterialTheme { + MetricCard( + title = "Active Sessions (Dark)", + value = "56", + icon = Icons.Default.Star, // Placeholder + colors = AppThemes.DarkThemeColors + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ReportItemC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ReportItemC.kt new file mode 100644 index 0000000..fb5cb9a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/ReportItemC.kt @@ -0,0 +1,98 @@ +package com.grtsinry43.activityanalyzer.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun ReportItem( + title: String, + period: String, + icon: ImageVector, + colors: AppThemes.Colors, + onDownload: () -> Unit, + onView: () -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(10.dp)) + .clickable { onView() } // Make the whole item clickable for viewing + .padding(vertical = 12.dp, horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = colors.accent, + modifier = Modifier.size(32.dp) // Larger icon for report items + ) + Spacer(modifier = Modifier.width(16.dp)) + Column(modifier = Modifier.weight(1f)) { + Text( + text = title, + fontSize = 16.sp, // Slightly larger title + color = colors.onSurface, + fontWeight = FontWeight.Medium + ) + Text( + text = period, + fontSize = 13.sp, + color = colors.secondaryText + ) + } + Spacer(modifier = Modifier.width(8.dp)) + IconButton(onClick = onDownload, modifier = Modifier.size(40.dp)) { + Icon( + imageVector = Icons.Default.Download, + contentDescription = "Download Report", + tint = colors.accentVariant + ) + } + } +} + +@Preview +@Composable +fun ReportItemPreview() { + MaterialTheme { + ReportItem( + title = "Weekly Summary", + period = "May 5 - May 11", + icon = Icons.Default.Edit, // Changed to avoid conflict with other previews + colors = AppThemes.LightThemeColors, + onDownload = {}, + onView = {} + ) + } +} + +@Preview +@Composable +fun ReportItemDarkPreview() { + MaterialTheme { + ReportItem( + title = "Monthly Summary (Dark)", + period = "April 2024", + icon = Icons.Default.Visibility, // Changed to avoid conflict + colors = AppThemes.DarkThemeColors, + onDownload = {}, + onView = {} + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SettingItemC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SettingItemC.kt new file mode 100644 index 0000000..5affdd2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SettingItemC.kt @@ -0,0 +1,136 @@ +package com.grtsinry43.activityanalyzer.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ChevronRight +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun SettingItem( + title: String, + subtitle: String, + icon: ImageVector, + colors: AppThemes.Colors, + onClick: (() -> Unit)? = null, + showSwitch: Boolean = false, + switchChecked: Boolean = false, + onSwitchChange: ((Boolean) -> Unit)? = null +) { + val itemModifier = if (onClick != null || showSwitch) { + Modifier.clickable { + if (showSwitch && onSwitchChange != null) { + onSwitchChange(!switchChecked) + } else { + onClick?.invoke() + } + } + } else { + Modifier + } + + Row( + modifier = Modifier + .fillMaxWidth() + .then(itemModifier) // Apply clickable if needed + .padding(horizontal = 16.dp, vertical = 14.dp), // Standard padding for list items + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = colors.accentVariant, + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(16.dp)) + Column(modifier = Modifier.weight(1f)) { + Text( + text = title, + fontSize = 16.sp, // Standard mobile list item title size + color = colors.onSurface, + fontWeight = FontWeight.Normal + ) + if (subtitle.isNotEmpty()) { + Text( + text = subtitle, + fontSize = 13.sp, + color = colors.secondaryText + ) + } + } + Spacer(modifier = Modifier.width(8.dp)) + if (showSwitch && onSwitchChange != null) { + Switch( + checked = switchChecked, + onCheckedChange = null, // Click handled by Row + colors = SwitchDefaults.colors( + checkedThumbColor = colors.accent, + checkedTrackColor = colors.accent.copy(alpha = 0.5f), + uncheckedThumbColor = colors.secondaryText, + uncheckedTrackColor = colors.border.copy(alpha = 0.5f) + ), + modifier = Modifier.size(40.dp) + ) + } else if (onClick != null && !showSwitch) { + Icon( + imageVector = Icons.Default.ChevronRight, + contentDescription = "Go to setting", + tint = colors.secondaryText.copy(alpha = 0.7f) + ) + } + } +} + +@Preview +@Composable +fun SettingItemPreview() { + MaterialTheme { + SettingItem( + title = "Display Settings", + subtitle = "Configure screen brightness and theme", + icon = Icons.Default.Settings, + colors = AppThemes.LightThemeColors, + onClick = {} + ) + } +} + +@Preview +@Composable +fun SettingItemWithSwitchPreview() { + MaterialTheme { + SettingItem( + title = "Enable Notifications", + subtitle = "Receive updates and alerts", + icon = Icons.Default.Settings, // Placeholder + colors = AppThemes.LightThemeColors, + showSwitch = true, + switchChecked = true, + onSwitchChange = {} + ) + } +} + +@Preview +@Composable +fun SettingItemDarkPreview() { + MaterialTheme { + SettingItem( + title = "Storage Settings (Dark)", + subtitle = "Manage local and cloud storage", + icon = Icons.Default.Settings, // Placeholder + colors = AppThemes.DarkThemeColors, + onClick = {} + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SimpleListItemC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SimpleListItemC.kt new file mode 100644 index 0000000..e42ae1f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/SimpleListItemC.kt @@ -0,0 +1,71 @@ +package com.grtsinry43.activityanalyzer.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.List +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.grtsinry43.activityanalyzer.theme.AppThemes +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun SimpleListItem( + icon: ImageVector, + text: String, + colors: AppThemes.Colors, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier.padding(vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = colors.accentVariant, + modifier = Modifier.size(22.dp) + ) + Text( + text, + style = MaterialTheme.typography.bodyLarge.copy( + color = colors.onSurface, + fontSize = 15.sp + ) + ) + } +} + +@Preview +@Composable +fun SimpleListItemPreview() { + MaterialTheme { + SimpleListItem( + icon = Icons.Default.List, + text = "Sample List Item", + colors = AppThemes.LightThemeColors + ) + } +} + +@Preview +@Composable +fun SimpleListItemDarkPreview() { + MaterialTheme { + SimpleListItem( + icon = Icons.Default.List, + text = "Sample List Item (Dark)", + colors = AppThemes.DarkThemeColors + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledButtonC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledButtonC.kt new file mode 100644 index 0000000..4b19506 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledButtonC.kt @@ -0,0 +1,72 @@ +package com.grtsinry43.activityanalyzer.components + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Favorite +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun StyledButton( + modifier: Modifier = Modifier, + icon: ImageVector, + text: String, + onClick: () -> Unit, + colors: AppThemes.Colors, + enabled: Boolean = true +) { + Button( + onClick = onClick, + modifier = modifier.height(48.dp), // Consistent height for mobile buttons + enabled = enabled, + shape = RoundedCornerShape(10.dp), + colors = ButtonDefaults.buttonColors( + containerColor = colors.accent.copy(alpha = 0.15f), // Slightly more opaque for visibility + contentColor = colors.accent, + disabledContainerColor = colors.border.copy(alpha = 0.1f), + disabledContentColor = colors.secondaryText.copy(alpha = 0.7f) + ), + border = BorderStroke(1.dp, colors.accent.copy(alpha = 0.4f)), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 10.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Icon(imageVector = icon, contentDescription = null, modifier = Modifier.size(20.dp)) + Text(text = text, fontSize = 14.sp, fontWeight = FontWeight.Medium) + } + } +} + +@Preview +@Composable +fun StyledButtonPreview() { + StyledButton( + icon = Icons.Default.Favorite, + text = "Preview Button", + onClick = {}, + colors = AppThemes.LightThemeColors + ) +} + +@Preview +@Composable +fun StyledButtonDarkPreview() { + StyledButton( + icon = Icons.Default.Favorite, + text = "Preview Button Dark", + onClick = {}, + colors = AppThemes.DarkThemeColors + ) +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledCard.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledCard.kt new file mode 100644 index 0000000..518ffa2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/components/StyledCard.kt @@ -0,0 +1,45 @@ +package com.grtsinry43.activityanalyzer.components + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Text +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun StyledCard( + modifier: Modifier = Modifier, + colors: AppThemes.Colors, + content: @Composable ColumnScope.() -> Unit +) { + Card( + modifier = modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)), // Slightly more rounded for mobile + colors = CardDefaults.cardColors( + containerColor = colors.surface + ), + elevation = CardDefaults.cardElevation(defaultElevation = 2.dp), // Slightly more elevation for mobile + border = BorderStroke(1.dp, colors.border.copy(alpha = 0.4f)), + content = content + ) +} + +@Preview +@Composable +fun StyledCardPreview() { + StyledCard( + colors = AppThemes.LightThemeColors + ) { + // Preview content + Text("123") + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAboutScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAboutScreenC.kt new file mode 100644 index 0000000..251e626 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAboutScreenC.kt @@ -0,0 +1,165 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +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.ui.Alignment +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 androidx.compose.ui.unit.sp +import com.grtsinry43.activityanalyzer.components.InfoItem +import com.grtsinry43.activityanalyzer.components.SettingItem +import com.grtsinry43.activityanalyzer.theme.AppThemes +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun MobileAboutScreen(colors: AppThemes.Colors) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon( + imageVector = Icons.Filled.Analytics, // App Icon + contentDescription = "App Logo", + tint = colors.accent, + modifier = Modifier.size(72.dp) // Slightly smaller for mobile about screen + ) + Text( + "Activity Analyzer", + style = MaterialTheme.typography.headlineMedium.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold, + fontSize = 24.sp + ) + ) + Text( + "Version 1.0.0-beta", + style = MaterialTheme.typography.titleMedium.copy( + color = colors.secondaryText, + fontSize = 16.sp + ) + ) + + Text( + "Your personal screen time companion, helping you understand and manage your digital habits across platforms.", + style = MaterialTheme.typography.bodyLarge.copy( + color = colors.onSurface, + fontSize = 15.sp + ), + textAlign = TextAlign.Center, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp) + ) + + // Developer Info Section + Text( + "Developer", + style = MaterialTheme.typography.titleMedium.copy( + color = colors.accent, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp + ), + modifier = Modifier.align(Alignment.Start).padding(top = 8.dp) + ) + InfoItem(icon = Icons.Filled.Person, text = "grtsinry43", colors = colors, onClick = {}) + Divider(color = colors.border.copy(alpha = 0.2f)) + InfoItem( + icon = Icons.Filled.Email, + text = "grtsinry43@outlook.com", + colors = colors, + isLink = true, + linkUrl = "mailto:grtsinry43@outlook.com", + onClick = {}) + Divider(color = colors.border.copy(alpha = 0.2f)) + InfoItem( + icon = Icons.Filled.Language, + text = "blog.grtsinry43.com", + colors = colors, + isLink = true, + linkUrl = "https://blog.grtsinry43.com", + onClick = {}) + Divider(color = colors.border.copy(alpha = 0.2f)) + InfoItem( + icon = Icons.Filled.Code, + text = "github.com/grtsinry43", + colors = colors, + isLink = true, + linkUrl = "https://github.com/grtsinry43", + onClick = {}) + + + // Application Info Section + Text( + "Application Info", + style = MaterialTheme.typography.titleMedium.copy( + color = colors.accent, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp + ), + modifier = Modifier.align(Alignment.Start).padding(top = 16.dp) + ) + SettingItem( + title = "Check for Updates", + subtitle = "Last checked: Today", + icon = Icons.Default.SystemUpdateAlt, + colors = colors, + onClick = {}) + Divider( + color = colors.border.copy(alpha = 0.2f), + modifier = Modifier.padding(horizontal = 16.dp) + ) // Use full width divider for settings like items + SettingItem( + title = "Acknowledgements", + subtitle = "Libraries and resources", + icon = Icons.Default.FavoriteBorder, + colors = colors, + onClick = {}) + Divider( + color = colors.border.copy(alpha = 0.2f), + modifier = Modifier.padding(horizontal = 16.dp) + ) + SettingItem( + title = "License Information", + subtitle = "View license", + icon = Icons.Default.Gavel, + colors = colors, + onClick = {}) + + Spacer(modifier = Modifier.weight(1f)) + Text( + "© 2025 grtsinry43. All rights reserved.", + style = MaterialTheme.typography.bodySmall.copy( + color = colors.secondaryText, + fontSize = 12.sp + ), + textAlign = TextAlign.Center, + modifier = Modifier.padding(bottom = 8.dp, top = 16.dp) + ) + } +} + +@Preview +@Composable +fun MobileAboutScreenPreview() { + MaterialTheme { + MobileAboutScreen(colors = AppThemes.LightThemeColors) + } +} + +@Preview +@Composable +fun MobileAboutScreenDarkPreview() { + MaterialTheme { + MobileAboutScreen(colors = AppThemes.DarkThemeColors) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAnalyticsScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAnalyticsScreenC.kt new file mode 100644 index 0000000..9c082ed --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileAnalyticsScreenC.kt @@ -0,0 +1,191 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +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.* +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@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 + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + "Screen Time Analytics", + style = MaterialTheme.typography.headlineSmall.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold, + fontSize = 22.sp + ) + ) + + var expanded by remember { mutableStateOf(false) } + Box(modifier = Modifier.fillMaxWidth()) { // Make dropdown full width for better mobile UX + OutlinedButton( + onClick = { expanded = true }, + shape = MaterialTheme.shapes.medium, + modifier = Modifier.fillMaxWidth(), + contentPadding = PaddingValues(16.dp) + ) { + Text(selectedTimeRange, color = colors.accent, fontSize = 16.sp) + Spacer(Modifier.weight(1f)) + Icon( + Icons.Default.ArrowDropDown, + contentDescription = "Select time range", + tint = colors.accent + ) + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier.fillMaxWidth(0.9f) // Adjust width as needed + ) { + timeRanges.forEach { range -> + DropdownMenuItem( + text = { Text(range, fontSize = 16.sp) }, + onClick = { + selectedTimeRange = range + expanded = false + // TODO: Update analytics data + } + ) + } + } + } + + // 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", + icon = Icons.Default.Smartphone, + colors = colors, + modifier = Modifier.weight(1f) + ) + MetricCard( + title = "Avg Daily", + value = "3h 35m", + icon = Icons.Default.AvTimer, + colors = colors, + modifier = Modifier.weight(1f) + ) + } + Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { + MetricCard( + title = "Most Used", + value = "App A", + icon = Icons.Default.StarOutline, + colors = colors, + modifier = Modifier.weight(1f) + ) + MetricCard( + title = "Pickups", + value = "75", + 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", + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp + ) + ) + ChartPlaceholder( + text = "Daily Screen Time (Bar Chart)", + colors = colors, + modifier = Modifier.fillMaxWidth().height(180.dp) + ) // Slightly smaller charts for mobile + ChartPlaceholder( + text = "App Usage (Pie Chart)", + colors = colors, + modifier = Modifier.fillMaxWidth().height(180.dp) + ) + } + } + + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + "Analysis Tools", + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp + ) + ) + StyledButton( + modifier = Modifier.fillMaxWidth(), + icon = Icons.Default.PieChartOutline, + text = "App Usage Breakdown", + onClick = { /* TODO */ }, + colors = colors + ) + StyledButton( + modifier = Modifier.fillMaxWidth(), + icon = Icons.Default.AccessTime, + text = "Time of Day Analysis", + onClick = { /* TODO */ }, + colors = colors + ) + StyledButton( + modifier = Modifier.fillMaxWidth(), + icon = Icons.Default.TrackChanges, + text = "Set Usage Goals", + onClick = { /* TODO */ }, + colors = colors + ) + } + } + } +} + +@Preview +@Composable +fun MobileAnalyticsScreenPreview() { + MaterialTheme { + MobileAnalyticsScreen(colors = AppThemes.LightThemeColors) + } +} + +@Preview +@Composable +fun MobileAnalyticsScreenDarkPreview() { + MaterialTheme { + MobileAnalyticsScreen(colors = AppThemes.DarkThemeColors) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileHomeScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileHomeScreenC.kt new file mode 100644 index 0000000..5892949 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileHomeScreenC.kt @@ -0,0 +1,174 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +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.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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun MobileHomeScreen(colors: AppThemes.Colors) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), // Standard padding for mobile screens + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = "Welcome Back, grtsinry43!", + style = MaterialTheme.typography.headlineSmall.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold, + fontSize = 22.sp // Adjusted for mobile + ) + ) + + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = "Today's Screen Time", + style = MaterialTheme.typography.titleLarge.copy( // Larger title for emphasis + color = colors.onSurface, + fontWeight = FontWeight.SemiBold, + fontSize = 18.sp + ) + ) + Text( + text = "3h 45m", // Placeholder + style = MaterialTheme.typography.displayMedium.copy( // Prominent display + color = colors.accent, + fontWeight = FontWeight.Bold, + fontSize = 36.sp + ), + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + Text( + text = "You're on track with your daily goal!", // Placeholder + style = MaterialTheme.typography.bodyMedium.copy( + color = colors.secondaryText, + fontSize = 14.sp + ), + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + } + } + + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + text = "Quick Glance: Top Apps", + 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) + } + } + + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) // Spacing for buttons + ) { + Text( + text = "Quick Actions", + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp + ) + ) + StyledButton( // Full width buttons for mobile quick actions + modifier = Modifier.fillMaxWidth(), + icon = Icons.Default.HourglassTop, + text = "Start Focus Session", + onClick = { /* TODO */ }, + colors = colors + ) + StyledButton( + modifier = Modifier.fillMaxWidth(), + icon = Icons.Default.CalendarViewDay, // Changed icon + text = "View Today's Details", + onClick = { /* TODO */ }, + colors = colors + ) + } + } + + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + text = "Recent Insights", + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp + ) + ) + ActivityItem( + title = "Exceeded daily goal for App X.", + time = "Today, 2:30 PM", + icon = Icons.Default.WarningAmber, + colors = colors + ) + ActivityItem( + title = "Screen time 20% higher yesterday.", + time = "Insight from yesterday", + icon = Icons.Default.TrendingUp, + colors = colors + ) + } + } + } +} + +@Preview +@Composable +fun MobileHomeScreenPreview() { + MaterialTheme { + MobileHomeScreen(colors = AppThemes.LightThemeColors) + } +} + +@Preview +@Composable +fun MobileHomeScreenDarkPreview() { + MaterialTheme { + MobileHomeScreen(colors = AppThemes.DarkThemeColors) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileProfileScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileProfileScreenC.kt new file mode 100644 index 0000000..a502a84 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileProfileScreenC.kt @@ -0,0 +1,188 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +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.* +import androidx.compose.ui.Alignment +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 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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun MobileProfileScreen(colors: AppThemes.Colors) { + var nickname by remember { mutableStateOf("grtsinry43") } + var email by remember { mutableStateOf("grtsinry43@outlook.com") } + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(20.dp), + horizontalAlignment = Alignment.CenterHorizontally // Center content like avatar + ) { + Text( + "User Profile", + style = MaterialTheme.typography.headlineSmall.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold, + fontSize = 22.sp + ), + modifier = Modifier.align(Alignment.Start) // Align title to start + ) + + Icon( + imageVector = Icons.Filled.AccountCircle, + contentDescription = "User Avatar", + tint = colors.accent, + modifier = Modifier.size(120.dp) // Large avatar for profile screen + ) + // TODO: Add option to change avatar (e.g., an Edit icon button) + + OutlinedTextField( + value = nickname, + onValueChange = { nickname = it }, + label = { Text("Nickname") }, + singleLine = true, + modifier = Modifier.fillMaxWidth(), + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = colors.accent, + unfocusedBorderColor = colors.border, + focusedLabelColor = colors.accent, + cursorColor = colors.accent, + focusedTextColor = colors.onSurface, + unfocusedTextColor = colors.onSurface + ) + ) + OutlinedTextField( + value = email, + onValueChange = { email = it }, + label = { Text("Email") }, + singleLine = true, + modifier = Modifier.fillMaxWidth(), + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = colors.accent, + unfocusedBorderColor = colors.border, + focusedLabelColor = colors.accent, + cursorColor = colors.accent, + focusedTextColor = colors.onSurface, + unfocusedTextColor = colors.onSurface + ) + ) + StyledButton( + icon = Icons.Default.Save, + text = "Save Changes", + onClick = { /* TODO: Save profile changes */ }, + colors = colors, + modifier = Modifier.fillMaxWidth().padding(top = 8.dp) + ) + + // Overall Statistics Card + StyledCard(colors = colors, modifier = Modifier.padding(top = 16.dp)) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + "Overall Statistics", + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp + ) + ) + SimpleListItem( + icon = Icons.Filled.Timer, + text = "Total Time Tracked: 1250 hrs", + colors = colors + ) + SimpleListItem( + icon = Icons.Filled.CheckCircleOutline, + text = "Goals Met Streak: 15 days", + colors = colors + ) + SimpleListItem( + icon = Icons.Filled.EventAvailable, + text = "Joined: Jan 1, 2024", + colors = colors + ) + } + } + + // Account Actions + Column( + modifier = Modifier.padding(top = 16.dp).fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + "Account Actions", + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onBackground, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp + ), + modifier = Modifier.padding(bottom = 4.dp) + ) + StyledButton( + icon = Icons.Default.LockReset, + text = "Change Password", + onClick = { /* TODO */ }, + colors = colors, + modifier = Modifier.fillMaxWidth() + ) + Button( // Destructive action button styling + onClick = { /* TODO: Show confirmation dialog */ }, + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(10.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color.Red.copy(alpha = 0.1f), + contentColor = Color.Red.copy(alpha = 0.9f) + ), + border = BorderStroke(1.dp, Color.Red.copy(alpha = 0.3f)), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Icon( + Icons.Default.DeleteForever, + contentDescription = null, + modifier = Modifier.size(20.dp) + ) + Text("Delete Account", fontSize = 14.sp, fontWeight = FontWeight.Medium) + } + } + } + } +} + +@Preview +@Composable +fun MobileProfileScreenPreview() { + MaterialTheme { + MobileProfileScreen(colors = AppThemes.LightThemeColors) + } +} + +@Preview +@Composable +fun MobileProfileScreenDarkPreview() { + MaterialTheme { + MobileProfileScreen(colors = AppThemes.DarkThemeColors) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileReportsScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileReportsScreenC.kt new file mode 100644 index 0000000..d911a50 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileReportsScreenC.kt @@ -0,0 +1,104 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +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.* +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.ReportItem +import com.grtsinry43.activityanalyzer.theme.AppThemes +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun MobileReportsScreen(colors: AppThemes.Colors) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + "Screen Time Reports", + style = MaterialTheme.typography.headlineSmall.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold, + fontSize = 22.sp + ) + ) + IconButton(onClick = { /* TODO: Generate new report */ }) { // Icon button for mobile + Icon( + Icons.Default.Addchart, + contentDescription = "Generate New Report", + tint = colors.accent + ) + } + } + + // Example Reports - for mobile, a simple list is often best + ReportItem( + title = "Weekly Summary - May 5-11", + period = "Generated: May 12, 2025", + icon = Icons.Default.CalendarToday, + colors = colors, + onDownload = {}, + onView = {}) + Divider(color = colors.border.copy(alpha = 0.2f), thickness = 0.5.dp) + ReportItem( + title = "Monthly App Usage - April", + period = "Generated: May 1, 2025", + icon = Icons.Default.PieChart, + colors = colors, + onDownload = {}, + onView = {}) + Divider(color = colors.border.copy(alpha = 0.2f), thickness = 0.5.dp) + ReportItem( + title = "Q1 Device Pickups", + period = "Generated: April 5, 2025", + icon = Icons.Default.TouchApp, + colors = colors, + onDownload = {}, + onView = {}) + Divider(color = colors.border.copy(alpha = 0.2f), thickness = 0.5.dp) + ReportItem( + title = "Focus Session - Project X", + period = "Generated: May 10, 2025", + icon = Icons.Default.HourglassEmpty, + colors = colors, + onDownload = {}, + onView = {}) + + // Placeholder if no reports + // Box(modifier = Modifier.fillMaxSize().padding(top = 32.dp), contentAlignment = Alignment.Center) { + // Text("No reports generated yet.", color = colors.secondaryText, style = MaterialTheme.typography.bodyLarge) + // } + } +} + +@Preview +@Composable +fun MobileReportsScreenPreview() { + MaterialTheme { + MobileReportsScreen(colors = AppThemes.LightThemeColors) + } +} + +@Preview +@Composable +fun MobileReportsScreenDarkPreview() { + MaterialTheme { + MobileReportsScreen(colors = AppThemes.DarkThemeColors) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileSettingsScreenC.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileSettingsScreenC.kt new file mode 100644 index 0000000..70911d4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/screens/MobileSettingsScreenC.kt @@ -0,0 +1,182 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +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.* +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun MobileSettingsScreen( + colors: AppThemes.Colors, + isDarkTheme: Boolean, + onThemeChange: (Boolean) -> Unit +) { + var autoBackupEnabled by remember { mutableStateOf(true) } + var notificationsEnabled by remember { mutableStateOf(true) } + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(bottom = 16.dp), // Padding at the bottom for scrollable content + verticalArrangement = Arrangement.spacedBy(0.dp) // No space between items, handled by SettingItem padding and dividers + ) { + // General Group + SettingsGroupHeader(title = "General", colors = colors) + SettingItem( + title = "Dark Theme", + subtitle = if (isDarkTheme) "Enabled" else "Disabled", + icon = Icons.Default.Brightness6, + colors = colors, + showSwitch = true, + switchChecked = isDarkTheme, + onSwitchChange = onThemeChange + ) + Divider( + color = colors.border.copy(alpha = 0.2f), + modifier = Modifier.padding(horizontal = 16.dp) + ) + SettingItem( + title = "Data Storage", + subtitle = "Manage storage location", // Simplified for mobile + icon = Icons.Default.FolderOpen, + colors = colors, + onClick = { /* TODO */ } + ) + Divider( + color = colors.border.copy(alpha = 0.2f), + modifier = Modifier.padding(horizontal = 16.dp) + ) + SettingItem( + title = "Auto Backup", + subtitle = if (autoBackupEnabled) "Daily at 2:00 AM" else "Disabled", + icon = Icons.Default.SaveAlt, + colors = colors, + showSwitch = true, + switchChecked = autoBackupEnabled, + onSwitchChange = { autoBackupEnabled = it } + ) + Divider( + color = colors.border.copy(alpha = 0.2f), + modifier = Modifier.padding(horizontal = 16.dp) + ) + SettingItem( + title = "Cloud Sync", + subtitle = "Not Connected", + icon = Icons.Default.CloudQueue, + colors = colors, + onClick = { /* TODO */ } + ) + Divider( + color = colors.border.copy(alpha = 0.2f), + modifier = Modifier.padding(horizontal = 16.dp) + ) + SettingItem( + title = "Export Data", + subtitle = "Export activity data", + icon = Icons.Default.Output, + colors = colors, + onClick = { /* TODO */ } + ) + + // Tracking Group + SettingsGroupHeader(title = "Tracking", colors = colors) + SettingItem( + title = "Apps to Track", + subtitle = "All Apps", // Placeholder + icon = Icons.Default.AppBlocking, + colors = colors, + onClick = { /* TODO */ } + ) + Divider( + color = colors.border.copy(alpha = 0.2f), + modifier = Modifier.padding(horizontal = 16.dp) + ) + SettingItem( + title = "Tracking Sensitivity", + subtitle = "Medium", // Placeholder + icon = Icons.Default.Tune, + colors = colors, + onClick = { /* TODO */ } + ) + + // Notifications Group + SettingsGroupHeader(title = "Notifications", colors = colors) + SettingItem( + title = "Screen Time Limits", + subtitle = "Notify when limits exceeded", + icon = Icons.Default.NotificationsActive, + colors = colors, + showSwitch = true, + switchChecked = notificationsEnabled, + onSwitchChange = { notificationsEnabled = it } + ) + Divider( + color = colors.border.copy(alpha = 0.2f), + modifier = Modifier.padding(horizontal = 16.dp) + ) + SettingItem( + title = "Break Reminders", + subtitle = "Get reminded to take breaks", + icon = Icons.Default.SelfImprovement, + colors = colors, + onClick = { /* TODO */ } + ) + Divider( + color = colors.border.copy(alpha = 0.2f), + modifier = Modifier.padding(horizontal = 16.dp) + ) + SettingItem( + title = "Weekly Summary", + subtitle = "Notify every Monday", + icon = Icons.Default.MarkEmailRead, + colors = colors, + showSwitch = true, + switchChecked = true, // Placeholder + onSwitchChange = { /* TODO */ } + ) + } +} + +@Composable +fun SettingsGroupHeader(title: String, colors: AppThemes.Colors) { + Text( + text = title, + style = MaterialTheme.typography.titleSmall.copy( // Using titleSmall for group headers + color = colors.accent, // Use accent color for headers + fontWeight = FontWeight.SemiBold, + fontSize = 14.sp + ), + modifier = Modifier + .fillMaxWidth() + .background(colors.background) // Ensure header background matches screen + .padding(horizontal = 16.dp, vertical = 12.dp) // More padding for group headers + ) +} + +@Preview +@Composable +fun MobileSettingsScreenPreview() { + MaterialTheme { + MobileSettingsScreen(colors = AppThemes.LightThemeColors, isDarkTheme = false, onThemeChange = {}) + } +} + +@Preview +@Composable +fun MobileSettingsScreenDarkPreview() { + MaterialTheme { + MobileSettingsScreen(colors = AppThemes.DarkThemeColors, isDarkTheme = true, onThemeChange = {}) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/theme/AppThemes.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/theme/AppThemes.kt new file mode 100644 index 0000000..5fc7136 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/theme/AppThemes.kt @@ -0,0 +1,43 @@ +package com.grtsinry43.activityanalyzer.theme + +import androidx.compose.ui.graphics.Color + +// --- THEMES (Reused from Desktop App) --- +// 定义颜色主题,提供更传统的桌面应用外观 +object AppThemes { + data class Colors( + val background: Color, + val surface: Color, + val onSurface: Color, + val onBackground: Color, + val accent: Color, + val accentVariant: Color, // 用于细微强调或悬停状态 + val border: Color, + val secondaryText: Color, + val onAccent: Color // 强调背景上的文本/图标颜色 + ) + + val LightThemeColors = Colors( + background = Color(0xFFE0E0E0), // Light Gray // 浅灰色 + surface = Color(0xFFFFFFFF), // White // 白色 + onSurface = Color(0xFF212121), // Dark Gray // 深灰色 + onBackground = Color(0xFF212121), // Dark Gray // 深灰色 + accent = Color(0xFF0D47A1), // Dark Blue // 深蓝色 + accentVariant = Color(0xFF1565C0), // Medium Blue // 中蓝色 + border = Color(0xFFB0B0B0), // Medium Gray // 中灰色 + secondaryText = Color(0xFF757575), // Gray // 灰色 + onAccent = Color.White + ) + + val DarkThemeColors = Colors( + background = Color(0xFF212121), // Very Dark Gray // 非常深的灰色 + surface = Color(0xFF303030), // Dark Gray // 深灰色 + onSurface = Color(0xFFE0E0E0), // Light Gray // 浅灰色 + onBackground = Color(0xFFE0E0E0), // Light Gray // 浅灰色 + accent = Color(0xFF42A5F5), // Light Blue // 浅蓝色 + accentVariant = Color(0xFF64B5F6), // Lighter Blue // 更浅的蓝色 + border = Color(0xFF525252), // Medium Dark Gray // 中度深灰色 + secondaryText = Color(0xFFBDBDBD), // Light Gray // 浅灰色 + onAccent = Color.Black + ) +} \ 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/activityanalyzer/DesktopApp.kt index 8719066..1b0ffa2 100644 --- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/DesktopApp.kt +++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/DesktopApp.kt @@ -1,11 +1,9 @@ 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.foundation.text.BasicText -import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material3.* @@ -14,633 +12,171 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.jetbrains.compose.ui.tooling.preview.Preview -import androidx.compose.material3.Switch -import androidx.compose.material3.SwitchDefaults -import androidx.compose.material3.TextButton +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 @Composable @Preview fun DesktopApp() { - var selectedItem by remember { mutableStateOf(0) } - var isDarkTheme by remember { mutableStateOf(false) } - var isSidebarCollapsed by remember { mutableStateOf(false) } // 新增状态变量 + var selectedItem by remember { mutableStateOf(0) } // 记住当前选中的导航项索引 + var isDarkTheme by remember { mutableStateOf(false) } // 记住当前是否为暗色主题 + var isSidebarCollapsed by remember { mutableStateOf(false) } // 记住侧边栏是否折叠 - val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFF5F5F5) - val surfaceColor = if (isDarkTheme) Color(0xFF2D2D2D) else Color.White - val textColor = if (isDarkTheme) Color.White else Color.Black - val accentColor = Color(0xFF007AFF) + val currentColors = + if (isDarkTheme) AppThemes.DarkThemeColors else AppThemes.LightThemeColors // 根据主题选择颜色 Box( modifier = Modifier - .fillMaxSize() - .background(backgroundColor) + .fillMaxSize() // 填充整个可用空间 + .background(currentColors.background) // 设置背景色 ) { Row( - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize() // 主行布局,填充整个可用空间 ) { // Sidebar + // 侧边栏 Box( modifier = Modifier - .width(if (isSidebarCollapsed) 60.dp else 200.dp) // 动态宽度 - .fillMaxHeight() - .background(surfaceColor) - .padding(16.dp) + .width(if (isSidebarCollapsed) 70.dp else 240.dp) // 根据折叠状态调整宽度 + .fillMaxHeight() // 填充整个高度 + .background(currentColors.surface) // 设置侧边栏背景色 + .padding( + start = 8.dp, + end = 8.dp, + top = 16.dp, + bottom = 16.dp + ) // 设置内边距 ) { Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(8.dp) + modifier = Modifier.fillMaxHeight(), // 列布局,填充整个高度 + verticalArrangement = Arrangement.spacedBy(8.dp) // 子项垂直间距 ) { if (!isSidebarCollapsed) { Text( - "Activity Analyzer", - fontSize = 16.sp, - fontWeight = FontWeight.Bold, - color = textColor + "Activity Analyzer", // 应用标题 + fontSize = 18.sp, // 字体大小 + fontWeight = FontWeight.SemiBold, // 字体粗细 + color = currentColors.onSurface, // 文本颜色 + modifier = Modifier.padding( + horizontal = 8.dp, + vertical = 16.dp + ) // 标题内边距 + ) + } else { + // 折叠时显示应用Logo图标 + Icon( + Icons.Default.Analytics, // 应用Logo图标 (示例) + contentDescription = "App Logo", // 内容描述 + tint = currentColors.accent, // 图标颜色 + modifier = Modifier + .size(40.dp) // 图标大小 + .align(Alignment.CenterHorizontally) // 水平居中 + .padding(vertical = 16.dp) // 垂直内边距 ) - Spacer(modifier = Modifier.height(32.dp)) } // Navigation items - NavItem( - icon = Icons.Default.Home, - text = if (isSidebarCollapsed) "" else "Home", // 根据收缩状态显示文本 - isSelected = selectedItem == 0, - onClick = { selectedItem = 0 }, - textColor = textColor, - accentColor = accentColor, - isSidebarCollapsed = isSidebarCollapsed - ) - NavItem( - icon = Icons.Default.BarChart, - text = if (isSidebarCollapsed) "" else "Analytics", - isSelected = selectedItem == 1, - onClick = { selectedItem = 1 }, - textColor = textColor, - accentColor = accentColor, - isSidebarCollapsed = isSidebarCollapsed - ) - NavItem( - icon = Icons.Default.Dataset, - text = if (isSidebarCollapsed) "" else "Reports", - isSelected = selectedItem == 2, - onClick = { selectedItem = 2 }, - textColor = textColor, - accentColor = accentColor, - isSidebarCollapsed = isSidebarCollapsed - ) - NavItem( - icon = Icons.Default.Settings, - text = if (isSidebarCollapsed) "" else "Settings", - isSelected = selectedItem == 3, - onClick = { selectedItem = 3 }, - textColor = textColor, - accentColor = accentColor, - isSidebarCollapsed = isSidebarCollapsed + // 导航项列表 + 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 // 关于页 ) - Spacer(modifier = Modifier.weight(1f)) - - // Theme switch - if (!isSidebarCollapsed) { - Row( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(8.dp)) - .background(if (isDarkTheme) Color(0xFF3D3D3D) else Color(0xFFE8E8E8)) - .padding(8.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Text( - "Dark Theme", - color = textColor - ) - Switch( - checked = isDarkTheme, - onCheckedChange = { isDarkTheme = it }, - colors = SwitchDefaults.colors( - checkedThumbColor = accentColor, - checkedTrackColor = accentColor.copy(alpha = 0.5f) - ) - ) - } + navItems.forEachIndexed { index, (text, icon) -> + NavItem( + icon = icon, + text = if (isSidebarCollapsed) "" else text, // 折叠时不显示文本 + isSelected = selectedItem == index, // 判断是否选中 + onClick = { selectedItem = index }, // 点击事件,更新选中项 + colors = currentColors, + isSidebarCollapsed = isSidebarCollapsed + ) } + Spacer(modifier = Modifier.weight(1f)) // 弹性空间,将后续内容推到底部 + + // User Profile Section + // 用户资料区域 (侧边栏底部) + if (!isSidebarCollapsed) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 8.dp), + horizontalAlignment = Alignment.CenterHorizontally // 水平居中对齐 + ) { + Icon( + imageVector = Icons.Default.AccountCircle, // 用户头像图标 + contentDescription = "User Avatar", // 内容描述 + tint = currentColors.accent, // 图标颜色 + modifier = Modifier + .size(48.dp) // 图标大小 + .clip(CircleShape) // 圆形裁剪 + ) + Spacer(modifier = Modifier.height(8.dp)) // 垂直间距 + Text( + text = "grtsinry43", // 用户昵称 (占位符) + color = currentColors.onSurface, // 文本颜色 + fontSize = 14.sp, // 字体大小 + fontWeight = FontWeight.Medium // 字体粗细 + ) + } + Spacer(modifier = Modifier.height(16.dp)) // 垂直间距 + } + + // Collapse/Expand button + // 折叠/展开侧边栏按钮 IconButton( - onClick = { isSidebarCollapsed = !isSidebarCollapsed }, - modifier = Modifier.align(Alignment.CenterHorizontally) + onClick = { isSidebarCollapsed = !isSidebarCollapsed }, // 点击切换折叠状态 + modifier = Modifier.align(Alignment.CenterHorizontally) // 水平居中 + .size(40.dp) // 按钮大小 ) { Icon( - imageVector = if (isSidebarCollapsed) Icons.Default.ChevronRight else Icons.Default.ChevronLeft, - contentDescription = "Toggle Sidebar", - tint = textColor + imageVector = if (isSidebarCollapsed) Icons.Default.MenuOpen else Icons.Default.Menu, // 根据状态选择图标 + contentDescription = "Toggle Sidebar", // 内容描述 + tint = currentColors.onSurface // 图标颜色 ) } } } // Main content + // 主内容区域 Box( modifier = Modifier - .fillMaxSize() - .padding(24.dp) + .fillMaxSize() // 填充整个可用空间 + .padding(24.dp) // 内边距 ) { + // 根据选中的导航项显示不同的屏幕内容 when (selectedItem) { - 0 -> HomeScreen(textColor = textColor, surfaceColor = surfaceColor) - 1 -> AnalyticsScreen(textColor = textColor, surfaceColor = surfaceColor) - 2 -> ReportsScreen(textColor = textColor, surfaceColor = surfaceColor) - 3 -> SettingsScreen(textColor = textColor, surfaceColor = surfaceColor) + 0 -> HomeScreen(colors = currentColors) + 1 -> AnalyticsScreen(colors = currentColors) + 2 -> ReportsScreen(colors = currentColors) + 3 -> ProfileScreen(colors = currentColors) + 4 -> SettingsScreen( + colors = currentColors, + isDarkTheme = isDarkTheme, + onThemeChange = { isDarkTheme = it } // 主题切换回调 + ) + + 5 -> AboutScreen(colors = currentColors) } } } } -} - -@Composable -fun NavItem( - icon: ImageVector, - text: String, - isSelected: Boolean, - onClick: () -> Unit, - textColor: Color, - accentColor: Color, - isSidebarCollapsed: Boolean -) { - Box( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) // 更大的圆角 - .background(if (isSelected) accentColor.copy(alpha = 0.2f) else Color.Transparent) - .padding(vertical = 12.dp, horizontal = if (isSidebarCollapsed) 8.dp else 16.dp) - .clickable( - onClick = onClick, - indication = null, - interactionSource = remember { MutableInteractionSource() } - ), - contentAlignment = Alignment.CenterStart - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(12.dp) - ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = if (isSelected) accentColor else textColor, - modifier = Modifier.size(if (isSidebarCollapsed) 32.dp else 24.dp) // 动态调整图标大小 - ) - if (!isSidebarCollapsed) { - Text( - text = text, - style = MaterialTheme.typography.titleMedium.copy( - color = if (isSelected) accentColor else textColor, - fontSize = 16.sp, - fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal - ) - ) - } - } - } -} - -@Composable -fun HomeScreen(textColor: Color, surfaceColor: Color) { - Column( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(24.dp) - ) { - SelectionContainer { - Text( - text = "Welcome Back!", - style = MaterialTheme.typography.headlineMedium, - color = MaterialTheme.colorScheme.onBackground - ) - } - - // Quick actions - Card( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(16.dp)) - .height(150.dp), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surface - ) - ) { - Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - SelectionContainer { - Text( - text = "Quick Actions", - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onSurface - ) - } - Row( - horizontalArrangement = Arrangement.spacedBy(16.dp) - ) { - ActionButton( - icon = Icons.Default.Add, - text = "New Activity", - onClick = {/* TODO */ } - ) - ActionButton( - icon = Icons.Default.Upload, - text = "Import Data", - onClick = {/* TODO */ } - ) - } - } - } - } - -// // Recent activities -// Card( -// modifier = Modifier -// .fillMaxWidth() -// .clip(RoundedCornerShape(16.dp)), -// colors = CardDefaults.cardColors( -// containerColor = MaterialTheme.colorScheme.surface -// ) -// ) { -// Column( -// modifier = Modifier.padding(16.dp), -// verticalArrangement = Arrangement.spacedBy(16.dp) -// ) { -// Text( -// text = "Recent Activities", -// style = MaterialTheme.typography.titleMedium, -// color = MaterialTheme.colorScheme.onSurface -// ) -// ActivityItem( -// title = "Activity Report Generated", -// time = "2024-04-12 14:30", -// icon = Icons.Default.Description -// ) -// ActivityItem( -// title = "Data Imported", -// time = "2024-04-12 10:15", -// icon = Icons.Default.Upload -// ) -// } -// } -} - -@Composable -fun ActionButton( - icon: ImageVector, - text: String, - onClick: () -> Unit -) { - Box( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(Color(0xFF007AFF).copy(alpha = 0.1f)) - .clickable(onClick = onClick) - .padding(16.dp), - contentAlignment = Alignment.Center - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = Color(0xFF007AFF) - ) - Text( - text = text, - color = Color(0xFF007AFF), - fontSize = 14.sp - ) - } - } -} - -@Composable -fun ActivityItem( - title: String, - time: String, - icon: ImageVector -) { - Row( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(8.dp)) - .background(Color(0xFFF5F5F5)) - .padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp) - ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = Color(0xFF007AFF) - ) - Column { - Text( - text = title, - fontSize = 16.sp, - color = Color.Black - ) - Text( - text = time, - fontSize = 14.sp, - color = Color.Gray - ) - } - } -} - -@Composable -fun AnalyticsScreen(textColor: Color, surfaceColor: Color) { - Column( - modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(24.dp) - ) { - Text( - "Analytics", - fontSize = 32.sp, - fontWeight = FontWeight.Bold, - color = textColor - ) - - // Analysis tools card - Card( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(16.dp)) - .background(surfaceColor) - .padding(24.dp) - ) { - Column( - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - Text( - "Analysis Tools", - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - color = textColor - ) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(16.dp) - ) { - AnalysisToolButton( - icon = Icons.Default.Info, - text = "Time Analysis", - onClick = {/* TODO */ } - ) - AnalysisToolButton( - icon = Icons.Default.Info, - text = "Distribution Analysis", - onClick = {/* TODO */ } - ) - } - } - } - } -} - -@Composable -fun AnalysisToolButton( - icon: ImageVector, - text: String, - onClick: () -> Unit -) { - Box( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(Color(0xFF007AFF).copy(alpha = 0.1f)) - .clickable(onClick = onClick) - .padding(16.dp), - contentAlignment = Alignment.Center - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = Color(0xFF007AFF) - ) - Text( - text = text, - color = Color(0xFF007AFF), - fontSize = 14.sp - ) - } - } -} - -@Composable -fun ReportsScreen(textColor: Color, surfaceColor: Color) { - Column( - modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(24.dp) - ) { - Text( - "Reports", - fontSize = 32.sp, - fontWeight = FontWeight.Bold, - color = textColor - ) - - // Report list - Card( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(16.dp)) - .background(surfaceColor) - .padding(24.dp) - ) { - Column( - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - Text( - "My Reports", - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - color = textColor - ) - ReportItem( - title = "Monthly Activity Report", - period = "March 2024", - icon = Icons.Default.Info - ) - ReportItem( - title = "Weekly Activity Analysis", - period = "Week 12, 2024", - icon = Icons.Default.Info - ) - } - } - } -} - -@Composable -fun ReportItem( - title: String, - period: String, - icon: ImageVector -) { - Row( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(8.dp)) - .background(Color(0xFFF5F5F5)) - .padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp) - ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = Color(0xFF007AFF) - ) - Column { - Text( - text = title, - fontSize = 16.sp, - color = Color.Black - ) - Text( - text = period, - fontSize = 14.sp, - color = Color.Gray - ) - } - } - IconButton(onClick = { /* TODO */ }) { - Icon( - imageVector = Icons.Default.Info, - contentDescription = "Download", - tint = Color(0xFF007AFF) - ) - } - } -} - -@Composable -fun SettingsScreen(textColor: Color, surfaceColor: Color) { - Column( - modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(24.dp) - ) { - Text( - "Settings", - fontSize = 32.sp, - fontWeight = FontWeight.Bold, - color = textColor - ) - - // Settings options - Card( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(16.dp)) - .background(surfaceColor) - .padding(24.dp) - ) { - Column( - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - SettingItem( - title = "Data Storage Location", - subtitle = "Default Location", - icon = Icons.Default.Info, - onClick = {/* TODO */ } - ) - SettingItem( - title = "Auto Backup", - subtitle = "Daily", - icon = Icons.Default.Info, - showSwitch = true - ) - SettingItem( - title = "Notification Settings", - subtitle = "Enabled", - icon = Icons.Default.Info, - showSwitch = true - ) - } - } - } -} - -@Composable -fun SettingItem( - title: String, - subtitle: String, - icon: ImageVector, - onClick: (() -> Unit)? = null, - showSwitch: Boolean = false -) { - Row( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(8.dp)) - .background(Color(0xFFF5F5F5)) - .padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp) - ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = Color(0xFF007AFF) - ) - Column { - Text( - text = title, - fontSize = 16.sp, - color = Color.Black - ) - Text( - text = subtitle, - fontSize = 14.sp, - color = Color.Gray - ) - } - } - if (showSwitch) { - Switch( - checked = true, - onCheckedChange = {/* TODO */ }, - colors = SwitchDefaults.colors( - checkedThumbColor = Color(0xFF007AFF), - checkedTrackColor = Color(0xFF007AFF).copy(alpha = 0.5f) - ) - ) - } else if (onClick != null) { - TextButton(onClick = onClick) { - Text( - "Change", - color = Color(0xFF007AFF) - ) - } - } - } -} +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/components/NavItemC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/components/NavItemC.kt new file mode 100644 index 0000000..6d322ef --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/components/NavItemC.kt @@ -0,0 +1,132 @@ +package com.grtsinry43.activityanalyzer.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Home +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.grtsinry43.activityanalyzer.theme.AppThemes +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun NavItem( + icon: ImageVector, // 导航项图标 + text: String, // 导航项文本 + isSelected: Boolean, // 是否被选中 + onClick: () -> Unit, // 点击事件回调 + colors: AppThemes.Colors, // 当前颜色主题 + isSidebarCollapsed: Boolean // 侧边栏是否折叠 +) { + val interactionSource = remember { MutableInteractionSource() } // 用于自定义点击效果 + val backgroundColor = + if (isSelected) colors.accent.copy(alpha = 0.15f) else Color.Transparent // 选中时背景色高亮 + val contentColor = + if (isSelected) colors.accent else colors.onSurface.copy(alpha = 0.8f) // 选中时内容颜色使用强调色 + + Row( // 使用 Row 布局导航项 + modifier = Modifier + .fillMaxWidth() // 填充宽度 + .height(if (isSidebarCollapsed) 50.dp else 44.dp) // 根据折叠状态调整高度 + .clip(RoundedCornerShape(8.dp)) // 圆角 + .background(backgroundColor) // 背景色 + .clickable( // 设置点击事件 + onClick = onClick, + interactionSource = interactionSource, + indication = null // 不使用默认的点击涟漪效果,依赖背景色变化 + ) + .padding(horizontal = if (isSidebarCollapsed) 0.dp else 12.dp), // 折叠时水平内边距为0,使图标居中 + verticalAlignment = Alignment.CenterVertically, // 垂直居中对齐 + horizontalArrangement = if (isSidebarCollapsed) Arrangement.Center else Arrangement.Start // 折叠时水平居中,否则从左开始 + ) { + Icon( + imageVector = icon, + contentDescription = text.ifEmpty { null }, // 如果文本为空,则内容描述为null + tint = contentColor, // 图标颜色 + modifier = Modifier.size(if (isSidebarCollapsed) 28.dp else 22.dp) // 根据折叠状态调整图标大小 + ) + if (!isSidebarCollapsed) { // 如果侧边栏未折叠,则显示文本 + Spacer(modifier = Modifier.width(12.dp)) // 图标和文本之间的间距 + Text( + text = text, + color = contentColor, // 文本颜色 + fontSize = 15.sp, // 字体大小 + fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Normal // 选中时字体加粗 + ) + } + } +} + +@Preview +@Composable +fun NavItemSelectedPreview() { + MaterialTheme { + NavItem( + icon = Icons.Default.Home, + text = "Home", + isSelected = true, + onClick = {}, + colors = AppThemes.LightThemeColors, + isSidebarCollapsed = false + ) + } +} + +@Preview +@Composable +fun NavItemUnselectedPreview() { + MaterialTheme { + NavItem( + icon = Icons.Default.Home, + text = "Home", + isSelected = false, + onClick = {}, + colors = AppThemes.LightThemeColors, + isSidebarCollapsed = false + ) + } +} + +@Preview +@Composable +fun NavItemSelectedCollapsedPreview() { + MaterialTheme { + NavItem( + icon = Icons.Default.Home, + text = "Home", // Text won't be visible + isSelected = true, + onClick = {}, + colors = AppThemes.LightThemeColors, + isSidebarCollapsed = true + ) + } +} + +@Preview +@Composable +fun NavItemDarkSelectedPreview() { + MaterialTheme { + NavItem( + icon = Icons.Default.Home, + text = "Home", + isSelected = true, + onClick = {}, + colors = AppThemes.DarkThemeColors, + isSidebarCollapsed = false + ) + } +} diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt index bc42181..5e27ecf 100644 --- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt +++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt @@ -1,13 +1,120 @@ package com.grtsinry43.activityanalyzer -import androidx.compose.ui.window.Window -import androidx.compose.ui.window.application +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Analytics // 用一个合适的图标作为托盘图标 +import androidx.compose.material3.Icon +import androidx.compose.runtime.* +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.graphics.toComposeImageBitmap +import androidx.compose.ui.res.loadImageBitmap +import androidx.compose.ui.res.useResource +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.* +import java.awt.image.BufferedImage +import javax.imageio.ImageIO + fun main() = application { - Window( - onCloseRequest = ::exitApplication, - title = "Activity Analyzer", - ) { - DesktopApp() + // 管理窗口状态,例如可见性、位置、大小 + val windowState = rememberWindowState( + size = DpSize(1024.dp, 768.dp), // 设置窗口的初始大小 +// position = WindowPosition(Win) // 初始位置居中 + ) + + // 管理托盘状态 + val trayState = rememberTrayState() + var isWindowVisible by remember { mutableStateOf(true) } // 控制窗口是否可见 + + // 尝试加载自定义托盘图标 (推荐使用 .ico 或 .png) + // 请将 "tray_icon.png" 替换为你的图标文件名,并确保它在 resources 目录下 + // 如果加载失败,则使用默认图标 + val trayIconPainter = try { + useResource("tray_icon.png") { stream -> + BitmapPainter(loadImageBitmap(stream)) + } + } catch (e: Exception) { + println("警告:无法加载自定义托盘图标 'tray_icon.png'。将使用默认图标。错误: ${e.message}") + null // 稍后会处理 null 情况 } + + // 系统托盘设置 + // 只有当窗口第一次变为不可见时,或者托盘图标加载成功时,才显示托盘图标 + // 这样可以避免在没有图标的情况下尝试创建托盘 + if (!isWindowVisible || trayIconPainter != null) { + Tray( + state = trayState, + icon = trayIconPainter ?: BitmapPainter(createDefaultTrayIcon()), // 如果自定义图标加载失败,使用一个备用图标 + tooltip = "Activity Analyzer", // 鼠标悬停在托盘图标上时显示的提示文字 + menu = { + // 托盘菜单项 + Item( + if (isWindowVisible) "隐藏窗口" else "显示窗口", + onClick = { + isWindowVisible = !isWindowVisible + if (isWindowVisible) { + windowState.isMinimized = false // 如果窗口被最小化了,恢复它 + } + } + ) + Separator() // 分隔线 + Item( + "退出应用", + onClick = ::exitApplication // 点击后退出整个应用 + ) + } + ) + } + + + // 主窗口设置 + if (isWindowVisible) { + Window( + onCloseRequest = { + // 点击关闭按钮时,可以选择隐藏到托盘而不是直接退出 + isWindowVisible = false + // 或者,如果你希望点击关闭直接退出应用,则使用: + // exitApplication() + }, + state = windowState, // 应用窗口状态 + title = "Activity Analyzer", + resizable = true, // 允许用户调整窗口大小 (默认为 true) + // icon = painterResource("app_icon.png") // 可选:设置窗口左上角的图标和任务栏图标 + ) { + // 在这里设置窗口的最小尺寸 + // 这会影响用户能将窗口缩小到的最小程度 + window.minimumSize = java.awt.Dimension(600, 400) + + // 你之前创建的 DesktopApp UI + // Assuming DesktopApp is a Composable function defined elsewhere + DesktopApp() + } + } +} + +// 创建一个简单的默认托盘图标 (如果自定义图标加载失败) +// 这是一个备选方案,实际项目中强烈建议使用合适的图片资源 +private fun createDefaultTrayIcon(): ImageBitmap { // Change return type to ImageBitmap + // 使用 Compose 图标作为示例,实际中你应该加载一个图像文件 + // 这里我们创建一个简单的 BufferedImage 代替 + val width = 64 + val height = 64 + val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) + val g = image.createGraphics() + try { + // 简单绘制一个蓝色方块作为示例 + 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" 缩写 + } finally { + g.dispose() + } + // Convert BufferedImage to ImageBitmap before returning + return image.toComposeImageBitmap() } \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AboutScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AboutScreenC.kt new file mode 100644 index 0000000..6da697c --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AboutScreenC.kt @@ -0,0 +1,173 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +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.ui.Alignment +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun AboutScreen(colors: AppThemes.Colors) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + .verticalScroll(rememberScrollState()), // 允许垂直滚动 + verticalArrangement = Arrangement.spacedBy(20.dp), + horizontalAlignment = Alignment.CenterHorizontally // 内容水平居中 + ) { + Icon( + imageVector = Icons.Filled.Analytics, // 应用Logo图标 (示例) + contentDescription = "App Logo", // 内容描述 + tint = colors.accent, // 图标颜色 + modifier = Modifier.size(80.dp) // 图标大小 + ) + Text( + "Activity Analyzer", // 应用名称 + style = MaterialTheme.typography.headlineMedium.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold + ) + ) + Text( + "Version 1.0.0-beta", // 应用版本号 + style = MaterialTheme.typography.titleMedium.copy(color = colors.secondaryText) + ) + + Spacer(modifier = Modifier.height(8.dp)) // 间距 + + // App Description Card + // 应用描述卡片 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + "Your personal screen time companion, helping you understand and manage your digital habits across platforms.", // 应用描述 + style = MaterialTheme.typography.bodyLarge.copy(color = colors.onSurface), + textAlign = TextAlign.Center // 文本居中 + ) + } + } + + // Developer Info Card + // 开发者信息卡片 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + "Developer", // 开发者 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ), + modifier = Modifier.padding(bottom = 8.dp) + ) + InfoItem(icon = Icons.Filled.Person, text = "grtsinry43", colors = colors) // 开发者名称 + Divider(color = colors.border.copy(alpha = 0.3f)) + InfoItem( + icon = Icons.Filled.Email, + text = "grtsinry43@outlook.com", // 开发者邮箱 + colors = colors, + isLink = true, // 可点击链接 + linkUrl = "mailto:grtsinry43@outlook.com" // 邮箱链接 + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + InfoItem( + icon = Icons.Filled.Language, // 网站/博客图标 + text = "blog.grtsinry43.com", // 网站/博客地址 + colors = colors, + isLink = true, + linkUrl = "https://blog.grtsinry43.com" // 网站链接 + ) // 个人网站/博客 (可点击) + Divider(color = colors.border.copy(alpha = 0.3f)) + InfoItem( + icon = Icons.Filled.Code, // Github图标 + text = "github.com/grtsinry43", // Github用户名 + colors = colors, + isLink = true, + linkUrl = "https://github.com/grtsinry43" // Github链接 + ) // GitHub (可点击) + } + } + + // Application Info Card + // 应用信息卡片 + StyledCard(colors = colors) { // 应用信息卡片 + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + "Application Info", // 应用信息 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ), + modifier = Modifier.padding(bottom = 8.dp) + ) + SettingItem( // 检查更新 (复用 SettingItem) + title = "Check for Updates", // 检查更新 + subtitle = "Last checked: Today", // 上次检查时间 (占位符) + icon = Icons.Default.SystemUpdateAlt, // 更新图标 + colors = colors, + onClick = { /* TODO: Implement update check */ } // 实现更新检查逻辑 + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + SettingItem( // 致谢 + title = "Acknowledgements", // 致谢 + subtitle = "Libraries and resources used", // 使用的库和资源 + icon = Icons.Default.FavoriteBorder, // 爱心图标 (代表感谢) + colors = colors, + onClick = { /* TODO: Show acknowledgements dialog/screen */ } // 显示致谢对话框/屏幕 + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + SettingItem( // 许可证信息 + title = "License Information", // 许可证信息 + subtitle = "View application license", // 查看应用许可证 + icon = Icons.Default.Gavel, // 法槌图标 (代表法律/许可) + colors = colors, + onClick = { /* TODO: Show license */ } // 显示许可证 + ) + } + } + Spacer(modifier = Modifier.weight(1f)) // 弹性空间,将版权信息推到底部 + Text( + "© 2025 grtsinry43. All rights reserved.", // 版权信息 + style = MaterialTheme.typography.bodySmall.copy(color = colors.secondaryText), + textAlign = TextAlign.Center, + modifier = Modifier.padding(bottom = 8.dp) + ) + } +} + +@Preview +@Composable +fun AboutScreenPreview() { + MaterialTheme { + AboutScreen(colors = AppThemes.LightThemeColors) + } +} + +@Preview +@Composable +fun AboutScreenDarkPreview() { + MaterialTheme { + AboutScreen(colors = AppThemes.DarkThemeColors) + } +} diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AnalyticsScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AnalyticsScreenC.kt new file mode 100644 index 0000000..4c111e9 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/AnalyticsScreenC.kt @@ -0,0 +1,222 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +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.* +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun AnalyticsScreen(colors: AppThemes.Colors) { + var selectedTimeRange by remember { mutableStateOf("Last 7 Days") } // 默认选中的时间范围 + val timeRanges = // 可选的时间范围 + listOf("Today", "Yesterday", "Last 7 Days", "Last 30 Days", "Custom Range") + + Column( + modifier = Modifier + .fillMaxSize() + .padding(8.dp) + .verticalScroll(rememberScrollState()), // 允许垂直滚动 + verticalArrangement = Arrangement.spacedBy(20.dp) + ) { + Text( + "Screen Time Analytics", // 屏幕时间分析 + style = MaterialTheme.typography.headlineSmall.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold // 加粗 + ) + ) + + // Time Range Filter + // 时间范围筛选器 + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + "Time Range:", + style = MaterialTheme.typography.titleSmall.copy(color = colors.onBackground) + ) // 时间范围标签 + var expanded by remember { mutableStateOf(false) } // 下拉菜单是否展开 + Box { + OutlinedButton( + onClick = { expanded = true }, + shape = RoundedCornerShape(8.dp) // 圆角按钮 + ) { // 点击展开下拉菜单 + Text(selectedTimeRange, color = colors.accent) // 显示选中的时间范围 + Icon( + Icons.Default.ArrowDropDown, + contentDescription = "Select time range", // 内容描述 + tint = colors.accent + ) // 下拉箭头图标 + } + DropdownMenu( // 下拉菜单内容 + expanded = expanded, + onDismissRequest = { expanded = false } // 点击外部关闭下拉菜单 + ) { + timeRanges.forEach { range -> + DropdownMenuItem( + text = { Text(range) }, + onClick = { + selectedTimeRange = range // 更新选中的时间范围 + expanded = false // 关闭下拉菜单 + // TODO: Update analytics data based on range // 根据选中的时间范围更新分析数据 + } + ) + } + } + } + } + + // Key Metrics Cards + // 关键指标卡片 + Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { + MetricCard( + title = "Total Screen Time", + value = "25h 10m", // 示例数据 + icon = Icons.Default.Smartphone, + colors = colors, + modifier = Modifier.weight(1f) + ) // 总屏幕时间 + MetricCard( + title = "Avg Daily Time", + value = "3h 35m", // 示例数据 + icon = Icons.Default.AvTimer, + colors = colors, + modifier = Modifier.weight(1f) + ) // 平均每日时间 + } + Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { + MetricCard( + title = "Most Used App", + value = "App A (8h)", // 示例数据 + icon = Icons.Default.StarOutline, + colors = colors, + modifier = Modifier.weight(1f) + ) // 最常用应用 + MetricCard( + title = "Pickups", + value = "75 today", // 示例数据 + icon = Icons.Default.TouchApp, + colors = colors, + modifier = Modifier.weight(1f) + ) // 拿起次数 + } + + // Charts Section + // 图表部分 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + "Usage Patterns", // 使用模式 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ) + ) + // Placeholder for Daily/Weekly Screen Time Bar Chart + // 每日/每周屏幕时间柱状图占位符 + ChartPlaceholder( + text = "Daily/Weekly Screen Time (Bar Chart)", + colors = colors, + modifier = Modifier.fillMaxWidth().height(200.dp) + ) + Spacer(modifier = Modifier.height(16.dp)) + // Placeholder for App Usage Distribution Pie Chart + // 应用使用分布饼图占位符 + ChartPlaceholder( + text = "App Usage Distribution (Pie Chart)", + colors = colors, + modifier = Modifier.fillMaxWidth().height(200.dp) + ) + } + } + + // Analysis Tools + // 分析工具 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + "Analysis Tools", // 分析工具 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ) + ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + StyledButton( + modifier = Modifier.weight(1f), + icon = Icons.Default.PieChartOutline, // 应用细分图标 + text = "App Breakdown", // 应用细分 + onClick = { /* TODO */ }, + colors = colors + ) + StyledButton( + modifier = Modifier.weight(1f), + icon = Icons.Default.AccessTime, // 时段分析图标 + text = "Time of Day", // 时段分析 + onClick = { /* TODO */ }, + colors = colors + ) + } + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + StyledButton( + modifier = Modifier.weight(1f), + icon = Icons.Default.TrackChanges, // 设定目标图标 + text = "Usage Goals", // 设定目标 + onClick = { /* TODO */ }, + colors = colors + ) + StyledButton( + modifier = Modifier.weight(1f), + icon = Icons.Default.CompareArrows, // 比较时段图标 + text = "Compare Periods", // 比较时段 + onClick = { /* TODO */ }, + colors = colors + ) + } + } + } + } +} + +@Preview +@Composable +fun AnalyticsScreenPreview() { + MaterialTheme { + AnalyticsScreen(colors = AppThemes.LightThemeColors) + } +} + +@Preview +@Composable +fun AnalyticsScreenDarkPreview() { + MaterialTheme { + AnalyticsScreen(colors = AppThemes.DarkThemeColors) + } +} diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/HomeScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/HomeScreenC.kt new file mode 100644 index 0000000..b6ddfd9 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/HomeScreenC.kt @@ -0,0 +1,184 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.selection.SelectionContainer +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.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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun HomeScreen(colors: AppThemes.Colors) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(8.dp) + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(20.dp) + ) { + SelectionContainer { + Text( + text = "Welcome Back, grtsinry43!", + style = MaterialTheme.typography.headlineSmall.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold + ) + ) + } + + // Overview of today's screen time + // 今日屏幕时间概览 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = "Today's Screen Time", // 今日屏幕时间 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ) + ) + Text( + text = "3h 45m", // 示例数据 + style = MaterialTheme.typography.displaySmall.copy( + color = colors.accent, + fontWeight = FontWeight.Bold + ), + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + Text( + text = "You're on track with your daily goal!", // 示例消息 + style = MaterialTheme.typography.bodyMedium.copy(color = colors.secondaryText), + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + } + } + + // Quick Glance: Top Apps Today + // 今日热门应用速览 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + text = "Quick Glance: Top Apps Today", // 今日热门应用 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ) + ) + 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 + ) + } + } + + // Quick actions + // 快捷操作 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = "Quick Actions", // 快捷操作 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ) + ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + StyledButton( + modifier = Modifier.weight(1f), + icon = Icons.Default.HourglassTop, // 专注时段图标 + text = "Focus Session", // 专注时段 + onClick = { /* TODO: Start focus session */ }, + colors = colors + ) + StyledButton( + modifier = Modifier.weight(1f), + icon = Icons.Default.CalendarViewWeek, // 每周报告图标 + text = "Weekly Report", // 每周报告 + onClick = { /* TODO: Navigate to weekly report */ }, + colors = colors + ) + } + } + } + + // Recent Alerts/Insights + // 最近提醒/洞察 (例如:与上周比较) + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + text = "Recent Insights", // 最近洞察 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ) + ) + ActivityItem( + title = "You've exceeded your daily goal for App X.", // 您已超出App X的每日目标 + time = "Today, 2:30 PM", // 时间示例 + icon = Icons.Default.WarningAmber, // 警告图标 + colors = colors + ) + ActivityItem( + title = "Screen time was 20% higher yesterday.", // 昨天的屏幕时间增加了20% + time = "Insight from yesterday", // 来自昨天的洞察 + icon = Icons.Default.TrendingUp, // 上升趋势图标 + colors = colors + ) + } + } + } +} + +@Preview +@Composable +fun HomeScreenPreview() { + MaterialTheme { + HomeScreen(colors = AppThemes.LightThemeColors) + } +} + +@Preview +@Composable +fun HomeScreenDarkPreview() { + MaterialTheme { + HomeScreen(colors = AppThemes.DarkThemeColors) + } +} diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ProfileScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ProfileScreenC.kt new file mode 100644 index 0000000..047b090 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ProfileScreenC.kt @@ -0,0 +1,183 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +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.* +import androidx.compose.ui.Alignment +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun ProfileScreen(colors: AppThemes.Colors) { + var nickname by remember { mutableStateOf("grtsinry43") } // 用户昵称 + var email by remember { mutableStateOf("grtsinry43@outlook.com") } // 用户邮箱 (占位符) + + Column( + modifier = Modifier + .fillMaxSize() + .padding(8.dp) + .verticalScroll(rememberScrollState()), // 允许垂直滚动 + verticalArrangement = Arrangement.spacedBy(20.dp) + ) { + Text( + "User Profile", // 个人资料 + style = MaterialTheme.typography.headlineSmall.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold // 加粗 + ) + ) + + // Profile Details Card + // 个人资料详情卡片 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, // 水平居中 + verticalArrangement = Arrangement.spacedBy(16.dp) // 子项间距 + ) { + Icon( + imageVector = Icons.Filled.AccountCircle, // 用户头像图标 + contentDescription = "User Avatar", // 内容描述 + tint = colors.accent, // 图标颜色 + modifier = Modifier.size(100.dp) // 图标大小 + ) + // TODO: Add option to change avatar // 添加更换头像的选项 + + OutlinedTextField( // 昵称输入框 + value = nickname, + onValueChange = { nickname = it }, + label = { Text("Nickname") }, // 标签:昵称 + singleLine = true, // 单行输入 + modifier = Modifier.fillMaxWidth(), + colors = OutlinedTextFieldDefaults.colors( // 自定义输入框颜色 + focusedBorderColor = colors.accent, + unfocusedBorderColor = colors.border, + focusedLabelColor = colors.accent, + cursorColor = colors.accent + ) + ) + OutlinedTextField( // 邮箱输入框 + value = email, + onValueChange = { email = it }, + label = { Text("Email") }, // 标签:邮箱 + singleLine = true, + modifier = Modifier.fillMaxWidth(), + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = colors.accent, + unfocusedBorderColor = colors.border, + focusedLabelColor = colors.accent, + cursorColor = colors.accent + ) + ) + StyledButton( // 保存更改按钮 + icon = Icons.Default.Save, // 保存图标 + text = "Save Changes", // 保存更改 + onClick = { /* TODO: Save profile changes */ }, // 保存个人资料更改 + colors = colors, + modifier = Modifier.fillMaxWidth(0.6f) + .align(Alignment.CenterHorizontally) // 按钮宽度为父容器的60%,并居中 + ) + } + } + + // Overall Statistics Card + // 总体统计数据卡片 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + "Overall Statistics", // 总体统计 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ) + ) + SimpleListItem( + icon = Icons.Filled.Timer, // 计时器图标 + text = "Total Time Tracked: 1250 hours", // 总追踪时间 + colors = colors + ) + SimpleListItem( + icon = Icons.Filled.CheckCircleOutline, // 勾选图标 + text = "Goals Met Streak: 15 days", // 目标达成连胜天数 + colors = colors + ) + SimpleListItem( + icon = Icons.Filled.EventAvailable, // 日历图标 + text = "Joined: January 1, 2024", // 加入日期 + colors = colors + ) + } + } + + // Account Actions Card + // 账户操作卡片 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + "Account Actions", // 账户操作 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ) + ) + StyledButton( // 更改密码按钮 + icon = Icons.Default.LockReset, // 密码重置图标 + text = "Change Password", // 更改密码 + onClick = { /* TODO */ }, + colors = colors, + modifier = Modifier.fillMaxWidth() + ) + StyledButton( // 删除账户按钮 + icon = Icons.Default.DeleteForever, // 永久删除图标 + text = "Delete Account", // 删除账户 + onClick = { /* TODO: Show confirmation dialog */ }, // 显示确认对话框 + colors = AppThemes.Colors( // 删除按钮使用警示性颜色主题 + background = colors.background, + surface = colors.surface, + onSurface = colors.onSurface, + onBackground = colors.onBackground, + accent = Color.Red.copy(alpha = 0.7f), // 红色强调色 + accentVariant = Color.Red, + border = colors.border, + secondaryText = colors.secondaryText, + onAccent = Color.White + ), + modifier = Modifier.fillMaxWidth() + ) + } + } + } +} + +@Preview +@Composable +fun ProfileScreenPreview() { + MaterialTheme { + ProfileScreen(colors = AppThemes.LightThemeColors) + } +} + +@Preview +@Composable +fun ProfileScreenDarkPreview() { + MaterialTheme { + ProfileScreen(colors = AppThemes.DarkThemeColors) + } +} diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ReportsScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ReportsScreenC.kt new file mode 100644 index 0000000..34800ca --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/ReportsScreenC.kt @@ -0,0 +1,123 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +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.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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun ReportsScreen(colors: AppThemes.Colors) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(8.dp) + .verticalScroll(rememberScrollState()), // 允许垂直滚动 + verticalArrangement = Arrangement.spacedBy(20.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, // 两端对齐 + verticalAlignment = Alignment.CenterVertically // 垂直居中 + ) { + Text( + "Screen Time Reports", // 屏幕时间报告 + style = MaterialTheme.typography.headlineSmall.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold // 加粗 + ) + ) + StyledButton( + icon = Icons.Default.Addchart, // 生成报告图标 + text = "Generate New Report", // 生成新报告 + onClick = { /* TODO: Open report generation dialog */ }, // 打开报告生成对话框 + colors = colors + ) + } + + // Generated Reports List + // 已生成报告列表 + StyledCard(colors = colors) { // 报告列表卡片 + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) // 列表项之间的紧凑间距 + ) { + Text( + "Generated Reports", // 已生成报告 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ), + modifier = Modifier.padding(bottom = 8.dp) // 标题下方间距 + ) + ReportItem( // 报告项示例1 + title = "Weekly Summary - May 5-11, 2025", // 每周总结 + period = "Generated: May 12, 2025", // 生成日期 + icon = Icons.Default.CalendarToday, // 日历图标 + colors = colors, + onDownload = { /* TODO */ }, // 下载回调 + onView = { /* TODO */ } // 查看回调 + ) + Divider(color = colors.border.copy(alpha = 0.3f)) // 分隔线 + ReportItem( // 报告项示例2 + title = "Monthly App Usage - April 2025", // 每月应用使用情况 + period = "Generated: May 1, 2025", // 生成日期 + icon = Icons.Default.PieChart, // 饼图图标 + colors = colors, + onDownload = { /* TODO */ }, + onView = { /* TODO */ } + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + ReportItem( // 报告项示例3 + title = "Q1 Device Pickup Analysis", // Q1设备拿起分析 + period = "Generated: April 5, 2025", // 生成日期 + icon = Icons.Default.TouchApp, // 触摸应用图标 + colors = colors, + onDownload = { /* TODO */ }, + onView = { /* TODO */ } + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + ReportItem( // 报告项示例4 + title = "Focus Session Report - Project X", // 专注时段报告 - 项目X + period = "Generated: May 10, 2025", // 生成日期 + icon = Icons.Default.HourglassEmpty, // 沙漏图标 + colors = colors, + onDownload = { /* TODO */ }, + onView = { /* TODO */ } + ) + } + } + // Placeholder if no reports + // 如果没有报告,显示占位符 +// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { +// Text("No reports generated yet.", color = colors.secondaryText, style = MaterialTheme.typography.bodyLarge) +// } + } +} + +@Preview +@Composable +fun ReportsScreenPreview() { + MaterialTheme { + ReportsScreen(colors = AppThemes.LightThemeColors) + } +} + +@Preview +@Composable +fun ReportsScreenDarkPreview() { + MaterialTheme { + ReportsScreen(colors = AppThemes.DarkThemeColors) + } +} diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/SettingsScreenC.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/SettingsScreenC.kt new file mode 100644 index 0000000..d0959a2 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/screens/SettingsScreenC.kt @@ -0,0 +1,207 @@ +package com.grtsinry43.activityanalyzer.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +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.* +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 org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun SettingsScreen( + colors: AppThemes.Colors, // 当前颜色主题 + isDarkTheme: Boolean, // 当前是否为暗色主题 + onThemeChange: (Boolean) -> Unit // 主题更改回调 +) { + var autoBackupEnabled by remember { mutableStateOf(true) } // 自动备份是否启用 + var notificationsEnabled by remember { mutableStateOf(true) } // 通知是否启用 + var selectedTrackingApps by remember { mutableStateOf("All Apps") } // 要追踪的应用 (示例状态) + var trackingSensitivity by remember { mutableStateOf("Medium") } // 追踪灵敏度 (示例状态) + + Column( + modifier = Modifier + .fillMaxSize() + .padding(8.dp) + .verticalScroll(rememberScrollState()), // 允许垂直滚动,因为设置项可能很多 + verticalArrangement = Arrangement.spacedBy(20.dp) + ) { + Text( + "Application Settings", // 应用设置 + style = MaterialTheme.typography.headlineSmall.copy( + color = colors.onBackground, + fontWeight = FontWeight.Bold // 加粗 + ) + ) + + // General Settings Card + // 通用设置卡片 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) // 列表项之间的紧凑间距 + ) { + Text( + "General", // 通用 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ), + modifier = Modifier.padding(bottom = 8.dp) // 标题下方间距 + ) + SettingItem( // 暗色主题切换 + title = "Dark Theme", // 暗色主题 + subtitle = if (isDarkTheme) "Enabled" else "Disabled", // 已启用/已禁用 + icon = Icons.Default.Brightness6, // 主题图标 + colors = colors, + showSwitch = true, // 显示切换开关 + switchChecked = isDarkTheme, // 开关状态 + onSwitchChange = onThemeChange // 开关切换回调 + ) + Divider(color = colors.border.copy(alpha = 0.3f)) // 分隔线 + SettingItem( // 数据存储位置 + title = "Data Storage Location", // 数据存储位置 + subtitle = "/Users/grtsinry43/Documents/ActivityAnalyzer", // 示例路径 + icon = Icons.Default.FolderOpen, // 打开文件夹图标 + colors = colors, + onClick = { /* TODO: Open file dialog or path editor */ } // 打开文件对话框或路径编辑器 + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + SettingItem( // 自动备份 + title = "Auto Backup", // 自动备份 + subtitle = if (autoBackupEnabled) "Daily at 2:00 AM" else "Disabled", // 每日凌晨2点/已禁用 + icon = Icons.Default.SaveAlt, // 保存图标 + colors = colors, + showSwitch = true, + switchChecked = autoBackupEnabled, + onSwitchChange = { autoBackupEnabled = it } + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + SettingItem( // 云同步 + title = "Cloud Sync", // 云同步 + subtitle = "Not Connected", // 未连接 (占位符) + icon = Icons.Default.CloudQueue, // 云队列图标 + colors = colors, + onClick = { /* TODO: Cloud sync setup */ } // 云同步设置 + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + SettingItem( // 导出数据 + title = "Export Data", // 导出数据 + subtitle = "Export your activity data (CSV, JSON)", // 导出活动数据 (CSV, JSON) + icon = Icons.Default.Output, // 导出图标 + colors = colors, + onClick = { /* TODO: Data export options */ } // 数据导出选项 + ) + } + } + + // Tracking Settings Card + // 追踪设置卡片 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + "Tracking", // 追踪 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ), + modifier = Modifier.padding(bottom = 8.dp) + ) + SettingItem( // 要追踪的应用 + title = "Apps to Track", // 要追踪的应用 + subtitle = selectedTrackingApps, // 当前选中的应用 + icon = Icons.Default.AppBlocking, // 应用阻止图标 (或类似) + colors = colors, + onClick = { /* TODO: Open app selection dialog */ } // 打开应用选择对话框 + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + SettingItem( // 追踪灵敏度 + title = "Tracking Sensitivity", // 追踪灵敏度 + subtitle = "Ignore app opens shorter than: $trackingSensitivity", // 忽略短于...的应用打开 (示例) + icon = Icons.Default.Tune, // 调整图标 + colors = colors, + onClick = { /* TODO: Open sensitivity options */ } // 打开灵敏度选项 + ) + } + } + + // Notification Settings Card + // 通知设置卡片 + StyledCard(colors = colors) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + "Notifications", // 通知 + style = MaterialTheme.typography.titleMedium.copy( + color = colors.onSurface, + fontWeight = FontWeight.SemiBold + ), + modifier = Modifier.padding(bottom = 8.dp) + ) + SettingItem( // 屏幕时间限制通知 + title = "Screen Time Limits", // 屏幕时间限制 + subtitle = "Notify when daily/app limits exceeded", // 当超出每日/应用限制时通知 + icon = Icons.Default.NotificationsActive, // 活动通知图标 + colors = colors, + showSwitch = true, + switchChecked = notificationsEnabled, // 示例状态 + onSwitchChange = { notificationsEnabled = it } + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + SettingItem( // 休息提醒 + title = "Break Reminders", // 休息提醒 + subtitle = "Get reminded to take breaks", // 获取休息提醒 + icon = Icons.Default.SelfImprovement, // 自我提升图标 (代表休息) + colors = colors, + onClick = { /* TODO: Configure break reminders */ } // 配置休息提醒 + ) + Divider(color = colors.border.copy(alpha = 0.3f)) + SettingItem( // 每周总结通知 + title = "Weekly Summary Notification", // 每周总结通知 + subtitle = "Receive a summary every Monday", // 每周一接收总结 + icon = Icons.Default.MarkEmailRead, // 已读邮件图标 + colors = colors, + showSwitch = true, + switchChecked = true, // 示例状态,默认开启 + onSwitchChange = { /* TODO */ } + ) + } + } + } +} + +@Preview +@Composable +fun SettingsScreenPreview() { + MaterialTheme { + SettingsScreen( + colors = AppThemes.LightThemeColors, + isDarkTheme = false, + onThemeChange = {} + ) + } +} + +@Preview +@Composable +fun SettingsScreenDarkPreview() { + MaterialTheme { + SettingsScreen( + colors = AppThemes.DarkThemeColors, + isDarkTheme = true, + onThemeChange = {} + ) + } +} diff --git a/iosApp/iosApp/BottomBar.swift b/iosApp/iosApp/BottomBar.swift index ac13b00..11fe240 100644 --- a/iosApp/iosApp/BottomBar.swift +++ b/iosApp/iosApp/BottomBar.swift @@ -1,11 +1,11 @@ + import SwiftUI import Shared +// MARK: - BottomBar struct BottomBar: View { - // Use meaningful tab names @State private var selectedTab: Tab = .today - // Define Tabs with associated icons and names enum Tab: CaseIterable { case today, weekly, settings @@ -28,69 +28,61 @@ struct BottomBar: View { var body: some View { TabView(selection: $selectedTab) { - // Use ContentView for the "Today" tab ContentView() .tag(Tab.today) .tabItem { Label(Tab.today.title, systemImage: Tab.today.iconName) } - // Placeholder for Weekly view WeeklyView() .tag(Tab.weekly) .tabItem { Label(Tab.weekly.title, systemImage: Tab.weekly.iconName) } - // Placeholder for Settings view SettingsView() .tag(Tab.settings) .tabItem { Label(Tab.settings.title, systemImage: Tab.settings.iconName) } } - .accentColor(.purple) // Example: Set a custom accent color for the tab bar + .accentColor(.purple) } } -// --- Weekly View --- +// MARK: - WeeklyView struct WeeklyView: View { - // Placeholder data for weekly usage (e.g., hours per day) let weeklyData: [Double] = [3.5, 4.2, 5.1, 2.8, 6.0, 7.5, 4.8] let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] - let goalHours: Double = 8.0 // Example daily goal + let goalHours: Double = 8.0 - // Calculate max value for chart scaling var maxValue: Double { - (weeklyData.max() ?? goalHours) * 1.1 // Add 10% buffer + (weeklyData.max() ?? goalHours) * 1.1 } var body: some View { NavigationView { - // Use List for standard iOS layout List { Section("Usage Trend") { - // Bar Chart Visualization - HStack(alignment: .bottom, spacing: 15) { // Increased spacing + HStack(alignment: .bottom, spacing: 15) { ForEach(0.. goalHours ? Color.orange : Color.accentColor) // Highlight days over goal - // Scale height relative to the max value - .frame(height: max(1, CGFloat(weeklyData[index] / maxValue) * 150)) // Ensure min height, scale based on max - .cornerRadius(5) // Rounded corners for bars + .fill(weeklyData[index] > goalHours ? Color.orange : Color.accentColor) + .frame(height: max(1, CGFloat(weeklyData[index] / maxValue) * 150)) + .cornerRadius(5) Text(days[index]) .font(.caption) .foregroundColor(.secondary) } - .frame(maxWidth: .infinity) // Allow bars to take equal width + .frame(maxWidth: .infinity) } } - .frame(height: 220) // Adjust overall chart height - .padding(.vertical) // Add padding around the chart + .frame(height: 220) + .padding(.vertical) } Section("Summary") { @@ -114,33 +106,54 @@ struct WeeklyView: View { } } } - .listStyle(.insetGrouped) // Use inset grouped style + .listStyle(.insetGrouped) .navigationTitle("Weekly Stats") } .navigationViewStyle(StackNavigationViewStyle()) } - // Helper function to calculate weekly average func calculateWeeklyAverage() -> String { let totalHours = weeklyData.reduce(0, +) let averageHours = totalHours / Double(weeklyData.count) let formatter = DateComponentsFormatter() formatter.allowedUnits = [.hour, .minute] formatter.unitsStyle = .abbreviated - // Convert hours to seconds for formatter return formatter.string(from: TimeInterval(averageHours * 3600)) ?? "0m" } - // Helper property for days over goal var daysOverGoalCount: Int { weeklyData.filter { $0 > goalHours }.count } } -// --- Settings View --- +// MARK: - GreetingViewModel +class GreetingViewModel: ObservableObject { + @Published var greetingText = "加载中..." + private let greeting = Greeting() + + func loadGreeting() { + greetingText = "加载中..." + + Task { + do { + let result = try await greeting.greet() + await MainActor.run { + self.greetingText = result + } + } catch { + await MainActor.run { + self.greetingText = "错误: \(error.localizedDescription)" + } + } + } + } +} + +// MARK: - SettingsView struct SettingsView: View { + @StateObject private var viewModel = GreetingViewModel() @State private var usageReminders = true - @State private var darkModeEnabled = false // Example state + @State private var darkModeEnabled = false @State private var selectedLimit = "No Limit" let appLimits = ["No Limit", "1 hour", "2 hours", "Custom"] @@ -149,7 +162,7 @@ struct SettingsView: View { Form { Section("Notifications") { Toggle("Usage Reminders", isOn: $usageReminders) - Toggle("Goal Achievement Alerts", isOn: .constant(false)) // Added toggle + Toggle("Goal Achievement Alerts", isOn: .constant(false)) } Section("Usage Limits") { @@ -158,11 +171,11 @@ struct SettingsView: View { Text($0) } } - NavigationLink("App Specific Limits", destination: Text("App Limits Detail (Placeholder)")) // Added link + NavigationLink("App Specific Limits", destination: Text("App Limits Detail (Placeholder)")) } Section("Appearance") { - Toggle("Dark Mode", isOn: $darkModeEnabled) // Added toggle + Toggle("Dark Mode", isOn: $darkModeEnabled) NavigationLink("Accent Color", destination: Text("Color Picker (Placeholder)")) } @@ -175,11 +188,21 @@ struct SettingsView: View { } NavigationLink("Privacy Policy", destination: Text("Privacy Policy Details (Placeholder)")) } - Section("test") { - Text("Hello From Kotlin Multiplatform: \n\(Greeting().greet()) ") + + Section("Kotlin Multiplatform 测试") { + Text("来自KMP的问候:\n\(viewModel.greetingText)") + .padding(.vertical, 8) + + Button("重新加载问候语") { + viewModel.loadGreeting() + } + .foregroundColor(.accentColor) } } .navigationTitle("Settings") + .onAppear { + viewModel.loadGreeting() + } } .navigationViewStyle(StackNavigationViewStyle()) }