Files
packages_apps_GameBar/src/com/android/gamebar/GpuUsageGraphView.kt
kenway214 aca1b4d4ed Initial Import
Signed-off-by: kenway214 <kenway214@outlook.com>
2025-10-24 20:18:59 +05:30

205 lines
6.5 KiB
Kotlin

/*
* SPDX-FileCopyrightText: 2025 kenway214
* SPDX-License-Identifier: Apache-2.0
*/
package com.android.gamebar
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import kotlin.math.max
class GpuUsageGraphView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val linePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#9C27B0") // Purple for GPU
strokeWidth = 3f
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
strokeJoin = Paint.Join.ROUND
}
private val fillPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
shader = LinearGradient(
0f, 0f, 0f, 600f,
Color.parseColor("#809C27B0"),
Color.parseColor("#00000000"),
Shader.TileMode.CLAMP
)
style = Paint.Style.FILL
}
private val gridPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#30FFFFFF")
strokeWidth = 1f
style = Paint.Style.STROKE
}
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#FFFFFF")
textSize = 28f
typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
}
private val avgLinePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#FF9800")
strokeWidth = 2f
style = Paint.Style.STROKE
pathEffect = DashPathEffect(floatArrayOf(10f, 10f), 0f)
}
private var gpuData: List<Pair<Long, Double>> = emptyList()
private var avgGpu: Double = 0.0
private val maxGpu = 100.0
private val minGpu = 0.0
private val padding = 80f
private val topPadding = 40f
private val bottomPadding = 80f
fun setData(data: List<Pair<Long, Double>>, avg: Double) {
this.gpuData = data
this.avgGpu = avg
post {
fillPaint.shader = LinearGradient(
0f, topPadding, 0f, height - bottomPadding,
Color.parseColor("#809C27B0"),
Color.parseColor("#109C27B0"),
Shader.TileMode.CLAMP
)
invalidate()
}
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (gpuData.isEmpty()) {
drawEmptyState(canvas)
return
}
val graphWidth = width - 2 * padding
val graphHeight = height - topPadding - bottomPadding
drawGrid(canvas, graphWidth, graphHeight)
drawAverageLine(canvas, graphWidth, graphHeight)
drawGraph(canvas, graphWidth, graphHeight)
drawTitle(canvas)
}
private fun drawEmptyState(canvas: Canvas) {
val message = "No GPU usage data available"
val textWidth = textPaint.measureText(message)
canvas.drawText(
message,
(width - textWidth) / 2,
height / 2f,
textPaint
)
}
private fun drawGrid(canvas: Canvas, graphWidth: Float, graphHeight: Float) {
val gpuSteps = listOf(0, 25, 50, 75, 100)
for (gpu in gpuSteps) {
val y = (topPadding + graphHeight * (1 - gpu / maxGpu.toFloat())).toFloat()
canvas.drawLine(padding, y, padding + graphWidth, y, gridPaint)
val label = "$gpu%"
canvas.drawText(label, padding - 70f, y + 10f, textPaint)
}
// Y-axis label
canvas.save()
canvas.rotate(-90f, 15f, height / 2f)
val yLabel = "GPU Usage (%)"
val yLabelWidth = textPaint.measureText(yLabel)
canvas.drawText(yLabel, (width - yLabelWidth) / 2, 30f, textPaint)
canvas.restore()
// Time labels
if (gpuData.isNotEmpty()) {
val startTime = gpuData.first().first
val endTime = gpuData.last().first
val duration = endTime - startTime
canvas.drawText("0s", padding, height - bottomPadding + 25f, textPaint)
val middleTime = formatDuration(duration / 2)
canvas.drawText(middleTime, padding + graphWidth / 2 - 30f, height - bottomPadding + 25f, textPaint)
val endTimeStr = formatDuration(duration)
val endX = padding + graphWidth - textPaint.measureText(endTimeStr)
canvas.drawText(endTimeStr, endX, height - bottomPadding + 25f, textPaint)
}
val xLabel = "Time"
val xLabelWidth = textPaint.measureText(xLabel)
canvas.drawText(xLabel, (width - xLabelWidth) / 2, height - bottomPadding + 55f, textPaint)
}
private fun drawAverageLine(canvas: Canvas, graphWidth: Float, graphHeight: Float) {
val y = (topPadding + graphHeight * (1 - avgGpu / maxGpu)).toFloat()
canvas.drawLine(padding, y, padding + graphWidth, y, avgLinePaint)
}
private fun drawGraph(canvas: Canvas, graphWidth: Float, graphHeight: Float) {
if (gpuData.size < 2) return
val startTime = gpuData.first().first
val endTime = gpuData.last().first
val timeDuration = max(endTime - startTime, 1L)
val path = Path()
val fillPath = Path()
gpuData.forEachIndexed { index, (timestamp, gpu) ->
val x = padding + ((timestamp - startTime).toFloat() / timeDuration) * graphWidth
val y = (topPadding + graphHeight * (1 - gpu / maxGpu)).toFloat()
if (index == 0) {
path.moveTo(x, y)
fillPath.moveTo(x, height - bottomPadding)
fillPath.lineTo(x, y)
} else {
path.lineTo(x, y)
fillPath.lineTo(x, y)
}
}
fillPath.lineTo(padding + graphWidth, height - bottomPadding)
fillPath.close()
canvas.drawPath(fillPath, fillPaint)
canvas.drawPath(path, linePaint)
}
private fun drawTitle(canvas: Canvas) {
val title = "GPU Usage vs Time"
val titleWidth = textPaint.measureText(title)
canvas.drawText(title, (width - titleWidth) / 2, topPadding - 10f, textPaint)
}
private fun formatDuration(durationMs: Long): String {
val seconds = durationMs / 1000
return if (seconds < 60) {
"${seconds}s"
} else {
val minutes = seconds / 60
val remainingSeconds = seconds % 60
"${minutes}m ${remainingSeconds}s"
}
}
}