feat: Introduce Network Functionality and Screen Time Implementation

- Implement `NetworkModule` for HTTP client management with JSON serialization.
- Add `kotlinx-serialization-json` and `ktor-client` dependencies.
- Update `Greeting` class to perform network requests to fetch and display time data.
- Add `NetworkResponse` and `TimeResponse` data classes for handling the remote API response.
- Introduce `ScreenTime` interface for platform-specific screen time data.
- Implement `ScreenTime` for Linux and Android, with basic or placeholder logic.
- Add `INTERNET` and `ACCESS_NETWORK_STATE` permissions to Android's `AndroidManifest.xml`.
- Add `ScreenTime.native.kt`, `ScreenTime.jvm.kt` and `ScreenTime.android.kt` to separate platform-specific code.
- Implement `kotlin("plugin.serialization")` plugin in shared module.
- Update dependencies.
- Add `ignoreUnknownKeys` and `coerceInputValues` for JSON serialization.
- Update `App` to display time data from remote API.
This commit is contained in:
grtsinry43 2025-04-23 10:24:20 +08:00
parent 8acd72bd9e
commit 9550889bdf
Signed by: grtsinry43
GPG Key ID: F3305FB3A978C934
10 changed files with 93 additions and 18 deletions

View File

@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 添加网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 可选:访问网络状态权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"

View File

@ -19,15 +19,24 @@ import activityanalyzer.composeapp.generated.resources.compose_multiplatform
fun App() {
MaterialTheme {
var showContent by remember { mutableStateOf(false) }
var greetingText by remember { mutableStateOf("加载中...") }
LaunchedEffect(key1 = Unit) {
greetingText = try {
Greeting().greet()
} catch (e: Exception) {
"错误:${e.message}"
}
}
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Button(onClick = { showContent = !showContent }) {
Text("Click me!")
}
AnimatedVisibility(showContent) {
val greeting = remember { Greeting().greet() }
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Image(painterResource(Res.drawable.compose_multiplatform), null)
Text("Compose: $greeting")
Text("Compose: $greetingText")
}
}
}

View File

@ -6,17 +6,17 @@ android-targetSdk = "35"
androidx-activityCompose = "1.10.1"
androidx-appcompat = "1.7.0"
androidx-constraintlayout = "2.2.1"
androidx-core-ktx = "1.15.0"
androidx-core-ktx = "1.16.0"
androidx-espresso-core = "3.6.1"
androidx-lifecycle = "2.8.4"
androidx-material = "1.12.0"
androidx-test-junit = "1.2.1"
compose-multiplatform = "1.7.3"
junit = "4.13.2"
kotlin = "2.1.10"
kotlin = "2.1.20"
kotlinx-coroutines = "1.10.1"
kotlinxSerializationJson = "1.6.2"
ktorClientCore = "2.3.8"
kotlinxSerializationJson = "1.8.0"
ktorClientCore = "3.1.1"
[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }

View File

@ -4,6 +4,8 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
kotlin("plugin.serialization") version libs.versions.kotlin.get()
}
kotlin {

View File

@ -0,0 +1,8 @@
package com.grtsinry43.activityanalyzer
actual fun getScreenTime(): ScreenTime {
return object : ScreenTime {
override val screenTime: Long
get() = 0L // TODO: Implement the actual logic to get screen time on Android
}
}

View File

@ -8,29 +8,23 @@ import kotlinx.serialization.Serializable
class Greeting {
private val platform = getPlatform()
fun greet(): String {
// val response: NetworkResponse =
// NetworkModule.httpClient.get("https://wlbzx.grtsinry43.com/api/time").body()
// println("response is : $response")
return "Hello, ${platform.name}! "
// +"Current time is ${response.data.time}"
suspend fun greet(): String {
val response: NetworkResponse =
NetworkModule.httpClient.get("https://wlbzx.grtsinry43.com/api/time").body()
println("response is : $response")
return "Hello, ${platform.name}! " + "Current time is ${response.data.time}"
}
}
@Serializable
data class NetworkResponse(
@SerialName("code")
val code: Int,
@SerialName("message")
val message: String,
@SerialName("data")
val msg: String,
val data: TimeResponse
)
@Serializable
data class TimeResponse(
@SerialName("time")
val time: String,
@SerialName("count")
val count: Int
)

View File

@ -11,6 +11,8 @@ object NetworkModule {
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
coerceInputValues = true // 尝试强制转换不匹配的值类型
})
}
}

View File

@ -0,0 +1,12 @@
package com.grtsinry43.activityanalyzer
/**
* @author grtsinry43
* @date 2025/4/14 16:13
* @description 热爱可抵岁月漫长
*/
interface ScreenTime {
val screenTime: Long
}
expect fun getScreenTime(): ScreenTime

View File

@ -0,0 +1,38 @@
package com.grtsinry43.activityanalyzer
import java.io.BufferedReader
import java.io.InputStreamReader
/**
* @author grtsinry43
* @date 2025/4/14 16:30
* @description 热爱可抵岁月漫长
*/
class LinuxScreenTime : ScreenTime {
override val screenTime: Long
get() {
try {
println("===")
// Execute qdbus command to query active window information
val process = Runtime.getRuntime().exec("qdbus org.kde.KWin /KWin queryWindowInfo")
val reader = BufferedReader(InputStreamReader(process.inputStream))
val output = reader.readLines()
reader.close()
// Parse the output to find the caption (window title)
val captionLine = output.find { it.trim().startsWith("caption:") }
val windowTitle = captionLine?.substringAfter("caption:")?.trim()
if (windowTitle != null) {
println("Active Window Title: $windowTitle")
return 3600 // Example: Return dummy screen time
}
} catch (e: Exception) {
e.printStackTrace()
}
println("Failed to get active window title.")
return 0
}
}
actual fun getScreenTime(): ScreenTime = LinuxScreenTime()

View File

@ -0,0 +1,5 @@
package com.grtsinry43.activityanalyzer
actual fun getScreenTime(): ScreenTime {
TODO("Not yet implemented")
}