Skip to content

Commit

Permalink
Creating test history page
Browse files Browse the repository at this point in the history
  • Loading branch information
SudoDios committed Oct 13, 2024
1 parent b01d1db commit 18c347c
Show file tree
Hide file tree
Showing 17 changed files with 612 additions and 11 deletions.
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ repositories {
}

dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
implementation(compose.desktop.currentOs)
api(compose.foundation)
api(compose.animation)
api("moe.tlaster:precompose:1.6.0")
implementation("org.jetbrains.compose.material3:material3-desktop:1.6.11")
implementation("dev.icerock.moko:mvvm-livedata-compose:0.16.1")
implementation("com.mikepenz:multiplatform-markdown-renderer:0.8.0")
implementation("org.slf4j:slf4j-log4j12:2.0.9")
}

compose.desktop {
Expand All @@ -30,6 +32,7 @@ compose.desktop {

nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
modules("java.sql")
packageName = "LibreSpeed"
packageVersion = "1.1.0"
val iconsRoot = project.file("src/main/resources")
Expand Down
Binary file added libs/sqlite-jdbc-3.46.1.3.jar
Binary file not shown.
15 changes: 15 additions & 0 deletions src/main/kotlin/LibreSpeed.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.toAwtImage
import androidx.compose.ui.platform.LocalDensity
Expand All @@ -12,15 +14,18 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.application
import core.Database
import core.Service
import moe.tlaster.precompose.PreComposeApp
import moe.tlaster.precompose.navigation.NavHost
import moe.tlaster.precompose.navigation.rememberNavigator
import moe.tlaster.precompose.navigation.transition.NavTransition
import routes.Route
import routes.scenes.HistoryScene
import routes.scenes.HomeScene
import routes.scenes.SplashScene
import theme.AppRippleTheme
import theme.ColorBox
import theme.Fonts
import java.awt.Dimension

Expand All @@ -36,6 +41,7 @@ fun App() {
) {
CompositionLocalProvider(LocalRippleTheme provides AppRippleTheme) {
NavHost(
modifier = Modifier.background(ColorBox.primaryDark),
navigator = navigator,
navTransition = NavTransition(),
initialRoute = Route.SPLASH,
Expand All @@ -52,6 +58,12 @@ fun App() {
) {
HomeScene(navigator)
}
scene(
route = Route.HISTORY,
navTransition = NavTransition()
) {
HistoryScene(navigator)
}
}
}
}
Expand All @@ -60,6 +72,9 @@ fun App() {
}

fun main() = application {
LaunchedEffect(Unit) {
Database.initDB()
}
Window(
onCloseRequest = ::exitApplication,
resizable = true,
Expand Down
106 changes: 106 additions & 0 deletions src/main/kotlin/components/TableView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package components

import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
import theme.ColorBox
import theme.Fonts

@Composable
fun TableView(
tableRows: List<TableItemRow>,
columnCount : Int,
modifier: Modifier,
itemContent: @Composable (column: Int,row : Int) -> String
) {

val scrollState = rememberLazyListState()

Column(modifier = modifier) {
TableItem(
modifier = Modifier.fillMaxWidth().height(64.dp).background(ColorBox.primary.copy(0.2f)).padding(start = 20.dp, end = 20.dp),
textStyle = MaterialTheme.typography.labelSmall.copy(fontFamily = Fonts.open_sans),
textColor = ColorBox.text,
list = tableRows.map { Triple(it.title,it.weight,it.textAlign) }
)
Box(Modifier.fillMaxWidth()) {
LazyColumn(Modifier.fillMaxWidth(), state = scrollState) {
items(columnCount) { column ->
val bg = if (column % 2 == 0) ColorBox.text.copy(0.03f) else Color.Transparent
TableItem(
modifier = Modifier.fillMaxWidth().height(58.dp).background(bg).padding(start = 20.dp, end = 20.dp),
textStyle = MaterialTheme.typography.bodyMedium.copy(fontFamily = Fonts.open_sans),
textColor = ColorBox.text,
list = tableRows.mapIndexed { index, tableItemRow -> Triple(itemContent(column,index),tableItemRow.weight,tableItemRow.textAlign) }
)
}
}
if (scrollState.canScrollForward || scrollState.canScrollBackward) {
VerticalScrollbar(
modifier = Modifier.align(Alignment.CenterEnd),
style = LocalScrollbarStyle.current
.copy(thickness = 5.dp, hoverColor = ColorBox.text.copy(0.6f), unhoverColor = ColorBox.text.copy(0.1f)),
adapter = rememberScrollbarAdapter(scrollState)
)
}
}
}

}

data class TableItemRow (
var weight : Float,
var title : String,
var textAlign: TextAlign
)

@Composable
private fun TableItem (modifier: Modifier,textColor: Color,textStyle : TextStyle,list: List<Triple<String,Float,TextAlign>>) {
val weights = remember { mutableStateListOf<Float>() }
var totalWeight by remember { mutableStateOf(0f) }
val textMeasurer = rememberTextMeasurer()
LaunchedEffect(list) {
weights.clear()
totalWeight = 0f
weights.addAll(list.map {
totalWeight += it.second
it.second
})
}
Canvas(modifier) {
if (weights.isNotEmpty()) {
var currentX = 0f
for (i in list.indices) {
val childWidth = (size.width * (weights[i] / totalWeight))
val textLayoutResult = textMeasurer.measure(
text = list[i].first,
style = textStyle.copy(textAlign = list[i].third),
maxLines = 2,
softWrap = true,
overflow = TextOverflow.Clip,
constraints = Constraints(0,childWidth.toInt(),0,size.height.toInt())
)
drawText(
textLayoutResult = textLayoutResult,
topLeft = Offset(currentX,size.height / 2 - textLayoutResult.size.height / 2),
color = textColor
)
currentX += childWidth
}
}
}
}
100 changes: 100 additions & 0 deletions src/main/kotlin/core/Database.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package core

import java.io.File
import java.nio.file.Paths
import java.sql.Connection
import java.sql.DriverManager
import java.util.*

object Database {

private const val APP_NAME: String = "librespeed-desktop"
private fun getDatabasePath(): String {
val osName = System.getProperty("os.name").lowercase(Locale.getDefault())
return if (osName.contains("linux")) {
Paths.get(System.getProperty("user.home"), ".local", "share", APP_NAME).toString()
} else if (osName.contains("windows")) {
System.getProperty("user.home") + File.separator + "AppData" + File.separator + "Roaming" + File.separator + APP_NAME
} else if (osName.contains("mac")) {
Paths.get(System.getProperty("user.home"), "Library", "Application Support", APP_NAME).toString()
} else {
throw UnsupportedOperationException("Unsupported operating system")
}
}

private lateinit var connection: Connection
fun initDB() {
File(getDatabasePath()).mkdirs()
val dbFile = File(getDatabasePath(), "librespeed.db")
Class.forName("org.sqlite.JDBC")
connection = DriverManager.getConnection("jdbc:sqlite:$dbFile")
createTables()
}

private fun createTables() {
val statement = connection.createStatement()
try {
statement.executeUpdate(
"CREATE TABLE IF NOT EXISTS history (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"netAdapter TEXT," +
"ping REAL," +
"jitter REAL," +
"download REAL," +
"upload REAL," +
"ispInfo TEXT," +
"testPoint TEXT," +
"date INTEGER" +
")"
)
} catch (_: Exception) {
} finally {
statement.close()
}
}
fun saveHistory(model: ModelHistory) {
val statement = connection.prepareStatement("INSERT INTO history (netAdapter,ping,jitter,download,upload,ispInfo,testPoint,date) VALUES (?,?,?,?,?,?,?,?)")
try {
statement.setString(1, model.netAdapter)
statement.setDouble(2, model.ping)
statement.setDouble(3, model.jitter)
statement.setDouble(4, model.download)
statement.setDouble(5, model.upload)
statement.setString(6, model.ispInfo)
statement.setString(7, model.testPoint)
statement.setLong(8, model.date)
statement.executeUpdate()
} catch (_ : Exception) {} finally {
statement.close()
}
}
fun readHistory() : MutableList<ModelHistory> {
val statement = connection.prepareStatement("SELECT * FROM history ORDER BY id DESC")
val resultSet = statement.executeQuery()
val result = mutableListOf<ModelHistory>()
while (resultSet.next()) {
result.add(
ModelHistory(
id = resultSet.getInt("id"),
netAdapter = resultSet.getString("netAdapter"),
ping = resultSet.getDouble("ping"),
jitter = resultSet.getDouble("jitter"),
download = resultSet.getDouble("download"),
upload = resultSet.getDouble("upload"),
ispInfo = resultSet.getString("ispInfo"),
testPoint = resultSet.getString("testPoint"),
date = resultSet.getLong("date"))
)
}
return result
}
fun clearHistory() {
val statement = connection.createStatement()
try {
statement.executeUpdate("DELETE FROM history")
} catch (_ : Exception) {} finally {
statement.close()
}
}

}
13 changes: 13 additions & 0 deletions src/main/kotlin/core/ModelHistory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package core

data class ModelHistory(
var id : Int = 0,
var netAdapter : String,
var ping : Double,
var jitter : Double,
var download : Double,
var upload : Double,
var ispInfo : String,
var testPoint : String,
var date : Long
)
24 changes: 23 additions & 1 deletion src/main/kotlin/core/Service.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import dev.icerock.moko.mvvm.livedata.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import util.NetUtils
import util.Utils.roundPlace
import util.Utils.toMegabyte
import util.Utils.validate
Expand All @@ -19,6 +20,7 @@ object Service {
const val UNIT_MBIT = "mbps"

val unitSetting = MutableLiveData(UNIT_MBIT)
val networkAdapter = MutableLiveData("")

val currentStep = MutableLiveData("")
val currentCalValue = MutableLiveData(0.0)
Expand Down Expand Up @@ -103,6 +105,12 @@ object Service {
} else {
running.value = true
CoroutineScope(Dispatchers.IO).launch {
val netInterface = NetUtils.getDefaultNetworkInterface()
if (netInterface != null) {
networkAdapter.value = "${netInterface.name} (${NetUtils.parseMacAddress(netInterface.hardwareAddress)})"
} else {
networkAdapter.value = "Unknown"
}
speedTestHandler.startTest(this@Service.testPoint.value,object : LibreSpeed.SpeedtestHandler() {
override fun onDownloadUpdate(dl: Double, progress: Double) {
currentStep.value = "DOWNLOAD"
Expand Down Expand Up @@ -150,7 +158,21 @@ object Service {
}
override fun onEnd() {
currentStep.value = "ENDED"
if (!running.value) reset() else goToResult.invoke()
if (!running.value) reset() else {
goToResult.invoke()
Database.saveHistory(
ModelHistory(
netAdapter = networkAdapter.value,
ping = ping.value.toDouble(),
jitter = jitter.value.toDouble(),
download = download.value,
upload = upload.value,
ispInfo = ipInfo.value,
testPoint = testPoint.value?.name.toString(),
date = System.currentTimeMillis()
)
)
}
running.value = false
}
override fun onCriticalFailure(err: String?) {
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/routes/Route.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ object Route {

const val SPLASH = "splash"
const val HOME = "home"
const val HISTORY = "history"

}
Loading

0 comments on commit 18c347c

Please sign in to comment.