package com.ivi.rs.widget
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
import com.airbnb.lottie.LottieAnimationView
import com.baselibrary.logger.Log
import com.ivi.remote.card.R
import kotlin.math.abs
import kotlin.math.floor
import kotlin.math.max
import kotlin.math.min
class RemoteBatteryView : View {
private var batteryPaint: Paint? = null
private var carLottieView: LottieAnimationView? = null
private var currentBatteryValue: Float
private var fullBitmap: Bitmap? = null
private var isChargeGunConnection: Boolean
private var lightShowRect: Rect? = null
private var maskBitmap: Bitmap? = null
private var porterDuffXfermode: PorterDuffXfermode? = null
private var showRect: Rect? = null
constructor(context: Context?) : super(context) {
currentBatteryValue = 0.0f
isChargeGunConnection = true
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
currentBatteryValue = 0.0f
isChargeGunConnection = true
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
currentBatteryValue = 0.0f
isChargeGunConnection = true
}
fun initBatterView(carLottieView: LottieAnimationView?) {
this.carLottieView = carLottieView
batteryPaint = Paint()
val options = BitmapFactory.Options()
options.inScaled = false
maskBitmap = BitmapFactory.decodeResource(resources, R.drawable.battery_mask, options)
fullBitmap = BitmapFactory.decodeResource(resources, R.drawable.battery_full_charge, options)
porterDuffXfermode = PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)
showRect = Rect(0, 0, 3552, SCREEN_HEIGHT)
lightShowRect = Rect(0, 0, 0, SCREEN_HEIGHT)
}
fun releaseResource() {
carLottieView = null
maskBitmap?.recycle()
fullBitmap?.recycle()
}
fun updateBattery(battery: Float) {
currentBatteryValue = battery
Log.i(TAG, "updateBattery battery = $battery")
val min = min(currentBatteryValue.toDouble(), 100.0).toFloat()
currentBatteryValue = min
currentBatteryValue = max(min.toDouble(), 0.0).toFloat()
}
fun updateChargeState(isCharge: Boolean) {
Log.i(TAG, "updateBattery isCharge = $isCharge")
isChargeGunConnection = isCharge
}
fun refreshChargeView() {
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (maskBitmap == null || maskBitmap?.isRecycled == true || fullBitmap == null || fullBitmap?.isRecycled == true) {
return
}
val abs = abs((((abs(NONE_BATTERY_POSITION.toDouble()) - abs(MAX_BATTERY_POSITION.toDouble())) / 100.0f) * currentBatteryValue).toDouble()).toInt()
lightShowRect!!.right = ((floor((currentBatteryValue / 6.25f).toDouble()).toInt()) * 100) + EMPTY_START_POSITION
showRect?.right = (4032 - abs((abs - 2418).toDouble())).toInt()
val saveLayer = canvas.saveLayer(0.0f, 0.0f, width.toFloat(), height.toFloat(), null)
val rect: Rect = showRect!!
canvas.drawBitmap(fullBitmap!!, rect, rect, batteryPaint)
batteryPaint!!.xfermode = porterDuffXfermode
canvas.drawBitmap(
maskBitmap!!,
(abs + NONE_BATTERY_POSITION + 50).toFloat(),
0.0f,
batteryPaint
)
batteryPaint!!.xfermode = null
val rect2: Rect = lightShowRect!!
canvas.drawBitmap(fullBitmap!!, rect2, rect2, batteryPaint)
canvas.restoreToCount(saveLayer)
var width = carLottieView!!.width - 89
val f = currentBatteryValue
val abs2 = (((abs(-1608.0) / 100.0f) * (if (f - 6.25f > 0.0f) f - 6.25f else 0.0f)).toInt()) + 1004
if (isChargeGunConnection) {
width = 0
}
val i = abs2 + width
carLottieView?.let {
it.translationX = (if (isChargeGunConnection) i else i - 215).toFloat()
}
carLottieView?.let {
it.scaleX = if (isChargeGunConnection) 1.0f else -1.0f
}
var i2 = i % ROTATION_ONE_STEP
if (!isChargeGunConnection) {
i2 = 184 - i2
}
carLottieView?.let {
val frame = i2 + 35
val progress = (frame - it.minFrame) / (it.maxFrame - it.minFrame)
Log.i(TAG, "onDraw frame: $frame, progress: $progress, minFrame: ${it.minFrame}, maxFrame: ${it.maxFrame}")
it.progress = progress
}
}
companion object {
const val BATTERY_ONE_STEP: Int = 100
const val BATTERY_OUT_WIDTH: Int = 1938
const val CAR_OFFSET: Int = 215
private const val CAR_PIC_TRIM_WIDTH = 89
const val EMPTY_START_POSITION: Int = 1272
const val MAS_ALPHA_POSITION: Int = 50
private const val MAX_BATTERY_POSITION = 2100
const val MAX_BATTERY_VALUE: Float = 100.0f
private const val MAX_CAR_POSITION = 70
const val MIN_BATTERY_VALUE: Float = 0.0f
private const val NONE_BATTERY_CAR_POSITION = 1004
private val NONE_BATTERY_POSITION = -480
private val NONE_CAR_POSITION = -1538
const val ONE_STEP_TO_BATTERY: Float = 6.25f
const val ROTATION_ONE_STEP: Int = 184
private const val SCREEN_HEIGHT = 284
private const val SCREEN_WIDTH = 4032
const val STOP_FRAME_NUM: Int = 35
private const val TAG = "RemoteBatteryView"
}
}
{"v":"5.12.1","fr":30,"ip":30,"op":220,"w":474,"h":180,"nm":"车 合成 1","ddd":0,"assets":[{"id":"image_0","w":421,"h":132,"u":"./","p":"car_lottie_resource.png","e":0},{"id":"image_1","w":474,"h":180,"u":"./","p":"car_lottie_resource_run.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":2,"nm":"左轮","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[0],"e":[1080]},{"t":600}],"ix":10},"p":{"a":0,"k":[125.5,125.25,0],"ix":2,"l":2},"a":{"a":0,"k":[110.5,90.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[16.983,0],[0,-16.983],[-16.983,0],[0,16.983]],"o":[[-16.983,0],[0,16.983],[16.983,0],[0,-16.983]],"v":[[110.5,59.5],[79.75,90.25],[110.5,121],[141.25,90.25]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"蒙版 1"}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":2,"nm":"右轮","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[0],"e":[1080]},{"t":600}],"ix":10},"p":{"a":0,"k":[360.562,124.312,0],"ix":2,"l":2},"a":{"a":0,"k":[345.562,89.312,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[16.672,0],[0,-16.672],[-16.672,0],[0,16.672]],"o":[[-16.672,0],[0,16.672],[16.672,0],[0,-16.672]],"v":[[345.562,59.125],[315.375,89.312],[345.562,119.5],[375.75,89.312]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"蒙版 1"}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":2,"nm":"car-2.png","cl":"png","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237,90,0],"ix":2,"l":2},"a":{"a":0,"k":[237,90,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}],"markers":[],"props":{}}
Car Model Rendering Issue Analysis Document
Involved Application
Android development application, includes two screen displays:
RemoteScreenActivity(displayed on display 1) ,loadsEnergyFragment,displays charging information and car model, uses Lottie animation.CardEditActivity(displayed on display 0)Problem Description
The car model changes normally with the battery level(refer to image:
battery_normal.png).Then start
CardEditActivity(display 0),the car model shows rendering errors as the battery level changes(refer to image:battery_wrong_1.png),After long-pressing the Home button to remove the
CardEditActivitytask, the car model returns to normal display as the battery level changes.Then start
RemoteScreenActivity(display 1), the car model changes normally with the battery level.At this point, long-press the Home button to remove the
CardEditActivitytask, and the car model shows rendering errors (refer to image:battery_wrong_2.png).After restarting
CardEditActivity, the car model returns to normal display as the battery level changes.Related Resource Images
Core Code
EnergyFragment.kt
EnergyView.kt
RemoteBatteryView.kt
lottie_animation.json
{"v":"5.12.1","fr":30,"ip":30,"op":220,"w":474,"h":180,"nm":"车 合成 1","ddd":0,"assets":[{"id":"image_0","w":421,"h":132,"u":"./","p":"car_lottie_resource.png","e":0},{"id":"image_1","w":474,"h":180,"u":"./","p":"car_lottie_resource_run.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":2,"nm":"左轮","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[0],"e":[1080]},{"t":600}],"ix":10},"p":{"a":0,"k":[125.5,125.25,0],"ix":2,"l":2},"a":{"a":0,"k":[110.5,90.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[16.983,0],[0,-16.983],[-16.983,0],[0,16.983]],"o":[[-16.983,0],[0,16.983],[16.983,0],[0,-16.983]],"v":[[110.5,59.5],[79.75,90.25],[110.5,121],[141.25,90.25]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"蒙版 1"}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":2,"nm":"右轮","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[0],"e":[1080]},{"t":600}],"ix":10},"p":{"a":0,"k":[360.562,124.312,0],"ix":2,"l":2},"a":{"a":0,"k":[345.562,89.312,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[16.672,0],[0,-16.672],[-16.672,0],[0,16.672]],"o":[[-16.672,0],[0,16.672],[16.672,0],[0,-16.672]],"v":[[345.562,59.125],[315.375,89.312],[345.562,119.5],[375.75,89.312]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"蒙版 1"}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":2,"nm":"car-2.png","cl":"png","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237,90,0],"ix":2,"l":2},"a":{"a":0,"k":[237,90,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}],"markers":[],"props":{}}