feedbackui
can show in recording
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package com.example.jnicpp.bowling
|
||||
|
||||
import android.text.Layout
|
||||
import android.text.TextPaint
|
||||
import android.text.StaticLayout
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.content.Context
|
||||
import android.graphics.Paint
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import com.example.jnicpp.R
|
||||
import android.graphics.BlurMaskFilter
|
||||
import android.graphics.Matrix
|
||||
import kotlin.math.min
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
|
||||
class FeedbackUI(private val context: Context, private val rootView: View) {
|
||||
private var landmarks: Map<Int, SmoothedLandmark>? = null
|
||||
private var CIRCLE_RADIUS = 128f
|
||||
private val uiTextSize = 32f
|
||||
private val uiStrokeWidth = 12f
|
||||
private val bannerPaddingY = 24
|
||||
private val bannerPaddingX = 24
|
||||
private val camScale = 0.5f
|
||||
|
||||
private var liveUiWidth: Int = 0
|
||||
private var liveUiHeight: Int = 0
|
||||
|
||||
private val glowPaint = Paint().apply {
|
||||
color = Color.RED
|
||||
style = Paint.Style.STROKE
|
||||
isAntiAlias = true
|
||||
strokeWidth = uiStrokeWidth // thicker stroke so glow is visible
|
||||
maskFilter = BlurMaskFilter(25f, BlurMaskFilter.Blur.OUTER)
|
||||
}
|
||||
|
||||
private val circlePaint = Paint().apply {
|
||||
color = Color.WHITE
|
||||
style = Paint.Style.STROKE
|
||||
isAntiAlias = true
|
||||
strokeWidth = uiStrokeWidth
|
||||
}
|
||||
|
||||
private val textPaint = TextPaint().apply {
|
||||
color = Color.WHITE
|
||||
textSize = uiTextSize
|
||||
isAntiAlias = true
|
||||
}
|
||||
|
||||
private val bgPaint = Paint().apply {
|
||||
color = Color.argb(64, 255, 255, 255)
|
||||
isAntiAlias = true
|
||||
}
|
||||
|
||||
init {
|
||||
rootView.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
setLiveUiSize(rootView)
|
||||
}
|
||||
}
|
||||
|
||||
fun showBanner(canvas: Canvas, message: String, forRecord: Boolean = false) {
|
||||
val scale = if (forRecord) computeCamScale(canvas) else 1f
|
||||
val paddingX = bannerPaddingX * scale
|
||||
val paddingY = bannerPaddingY * scale
|
||||
val maxWidth = (canvas.width * 0.8f).toInt()
|
||||
|
||||
// Build StaticLayout for multi-line text
|
||||
textPaint.textSize = uiTextSize * scale
|
||||
val staticLayout = StaticLayout.Builder
|
||||
.obtain(message, 0, message.length, textPaint, maxWidth)
|
||||
.setAlignment(Layout.Alignment.ALIGN_CENTER) // center text horizontally
|
||||
.setLineSpacing(0f, 1f)
|
||||
.setIncludePad(false)
|
||||
.build()
|
||||
|
||||
// Use StaticLayout dimensions
|
||||
val textWidth = staticLayout.width.toFloat()
|
||||
val textHeight = staticLayout.height.toFloat()
|
||||
|
||||
val bannerWidth = textWidth + paddingX * 2
|
||||
val bannerHeight = textHeight + paddingY * 2
|
||||
|
||||
// Center horizontally
|
||||
val left = (canvas.width - bannerWidth) / 2f
|
||||
val top = 172f * scale
|
||||
val right = left + bannerWidth
|
||||
val bottom = top + bannerHeight
|
||||
|
||||
// Draw background
|
||||
val rect = RectF(left, top, right, bottom)
|
||||
|
||||
// Scale corner radius too
|
||||
val cornerRadius = 24f * scale
|
||||
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, bgPaint)
|
||||
|
||||
// Draw text layout inside background
|
||||
canvas.save()
|
||||
canvas.translate(left + paddingX, top + paddingY)
|
||||
staticLayout.draw(canvas)
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
// --- Shape overlay (circle) ---
|
||||
fun drawCircles(canvas: Canvas, landmarks: Map<Int, SmoothedLandmark>, transform: Matrix, forRecord: Boolean = false) {
|
||||
for (landmark in landmarks.values) {
|
||||
val point = floatArrayOf(landmark.x, landmark.y)
|
||||
transform.mapPoints(point)
|
||||
drawCircle(canvas, point[0], point[1], 0f, forRecord)
|
||||
}
|
||||
}
|
||||
|
||||
fun drawCircle(canvas: Canvas, x: Float, y: Float, radius: Float = 0f, forRecord: Boolean = false) {
|
||||
val scale = if (forRecord) camScale else 1f
|
||||
|
||||
var rad = (if (radius == 0f) CIRCLE_RADIUS else radius) * scale
|
||||
circlePaint.strokeWidth = uiStrokeWidth * scale
|
||||
glowPaint.strokeWidth = uiStrokeWidth * scale
|
||||
canvas.drawCircle(x, y, rad, circlePaint)
|
||||
canvas.drawCircle(x, y, rad, glowPaint)
|
||||
}
|
||||
|
||||
// Store live UI size once
|
||||
fun setLiveUiSize(rootView: View) {
|
||||
liveUiWidth = rootView.width
|
||||
liveUiHeight = rootView.height
|
||||
}
|
||||
|
||||
// Compute scale when drawing to recording canvas
|
||||
private fun computeCamScale(recordCanvas: Canvas): Float {
|
||||
if (liveUiWidth == 0 || liveUiHeight == 0) return 1f
|
||||
val scaleX = recordCanvas.width.toFloat() / liveUiWidth.toFloat()
|
||||
val scaleY = recordCanvas.height.toFloat() / liveUiHeight.toFloat()
|
||||
return min(scaleX, scaleY)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user