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:
parent
8acd72bd9e
commit
9550889bdf
@ -1,6 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<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
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
|||||||
@ -19,15 +19,24 @@ import activityanalyzer.composeapp.generated.resources.compose_multiplatform
|
|||||||
fun App() {
|
fun App() {
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
var showContent by remember { mutableStateOf(false) }
|
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) {
|
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
Button(onClick = { showContent = !showContent }) {
|
Button(onClick = { showContent = !showContent }) {
|
||||||
Text("Click me!")
|
Text("Click me!")
|
||||||
}
|
}
|
||||||
AnimatedVisibility(showContent) {
|
AnimatedVisibility(showContent) {
|
||||||
val greeting = remember { Greeting().greet() }
|
|
||||||
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
Image(painterResource(Res.drawable.compose_multiplatform), null)
|
Image(painterResource(Res.drawable.compose_multiplatform), null)
|
||||||
Text("Compose: $greeting")
|
Text("Compose: $greetingText")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,17 +6,17 @@ android-targetSdk = "35"
|
|||||||
androidx-activityCompose = "1.10.1"
|
androidx-activityCompose = "1.10.1"
|
||||||
androidx-appcompat = "1.7.0"
|
androidx-appcompat = "1.7.0"
|
||||||
androidx-constraintlayout = "2.2.1"
|
androidx-constraintlayout = "2.2.1"
|
||||||
androidx-core-ktx = "1.15.0"
|
androidx-core-ktx = "1.16.0"
|
||||||
androidx-espresso-core = "3.6.1"
|
androidx-espresso-core = "3.6.1"
|
||||||
androidx-lifecycle = "2.8.4"
|
androidx-lifecycle = "2.8.4"
|
||||||
androidx-material = "1.12.0"
|
androidx-material = "1.12.0"
|
||||||
androidx-test-junit = "1.2.1"
|
androidx-test-junit = "1.2.1"
|
||||||
compose-multiplatform = "1.7.3"
|
compose-multiplatform = "1.7.3"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
kotlin = "2.1.10"
|
kotlin = "2.1.20"
|
||||||
kotlinx-coroutines = "1.10.1"
|
kotlinx-coroutines = "1.10.1"
|
||||||
kotlinxSerializationJson = "1.6.2"
|
kotlinxSerializationJson = "1.8.0"
|
||||||
ktorClientCore = "2.3.8"
|
ktorClientCore = "3.1.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.kotlinMultiplatform)
|
alias(libs.plugins.kotlinMultiplatform)
|
||||||
alias(libs.plugins.androidLibrary)
|
alias(libs.plugins.androidLibrary)
|
||||||
|
|
||||||
|
kotlin("plugin.serialization") version libs.versions.kotlin.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,29 +8,23 @@ import kotlinx.serialization.Serializable
|
|||||||
class Greeting {
|
class Greeting {
|
||||||
private val platform = getPlatform()
|
private val platform = getPlatform()
|
||||||
|
|
||||||
fun greet(): String {
|
suspend fun greet(): String {
|
||||||
// val response: NetworkResponse =
|
val response: NetworkResponse =
|
||||||
// NetworkModule.httpClient.get("https://wlbzx.grtsinry43.com/api/time").body()
|
NetworkModule.httpClient.get("https://wlbzx.grtsinry43.com/api/time").body()
|
||||||
// println("response is : $response")
|
println("response is : $response")
|
||||||
return "Hello, ${platform.name}! "
|
return "Hello, ${platform.name}! " + "Current time is ${response.data.time}"
|
||||||
// +"Current time is ${response.data.time}"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class NetworkResponse(
|
data class NetworkResponse(
|
||||||
@SerialName("code")
|
|
||||||
val code: Int,
|
val code: Int,
|
||||||
@SerialName("message")
|
val msg: String,
|
||||||
val message: String,
|
|
||||||
@SerialName("data")
|
|
||||||
val data: TimeResponse
|
val data: TimeResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TimeResponse(
|
data class TimeResponse(
|
||||||
@SerialName("time")
|
|
||||||
val time: String,
|
val time: String,
|
||||||
@SerialName("count")
|
|
||||||
val count: Int
|
val count: Int
|
||||||
)
|
)
|
||||||
@ -11,6 +11,8 @@ object NetworkModule {
|
|||||||
json(Json {
|
json(Json {
|
||||||
prettyPrint = true
|
prettyPrint = true
|
||||||
isLenient = true
|
isLenient = true
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
coerceInputValues = true // 尝试强制转换不匹配的值类型
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
@ -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()
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.grtsinry43.activityanalyzer
|
||||||
|
|
||||||
|
actual fun getScreenTime(): ScreenTime {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user