Commit a8ab6745 authored by p x's avatar p x
Browse files

添加居中提示字的逻辑

parent 4e15cda9
......@@ -13,7 +13,7 @@ android {
defaultConfig {
applicationId = "com.sd.cavphmi"
minSdk = 29
minSdk = 31
targetSdk = 35
versionCode = 1
versionName = "1.0"
......@@ -50,6 +50,17 @@ android {
abortOnError = false
checkReleaseBuilds = false
}
applicationVariants.all {
outputs.all {
if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
val config = project.android.defaultConfig
val versionName = config.versionName
// val formatter = DateTimeFormatter.ofPattern("yyyy_MM_dd_HHmm")
// val createTime = LocalDateTime.now().format(formatter)
outputFileName = "avp_${versionName}.apk"
}
}
}
}
......@@ -60,8 +71,9 @@ dependencies {
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx)
// implementation(libs.androidx.navigation.fragment.ktx)
// implementation(libs.androidx.navigation.ui.ktx)
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2")
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
......
package com.sd.cavphmi
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.google.gson.Gson
import com.sd.cavphmi.bean.mock.MRoutes
import com.sd.cavphmi.utils.FileIoUtils
import com.sd.cavphmi.utils.Proj4jCoord
import com.sd.cavphmi.utils.SM4CryptoHelper
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
......@@ -24,10 +29,60 @@ class ExampleInstrumentedTest {
}
@Test
fun loginCpy(){
var pwd="vUO2dStZDhbd*88FfT84"
var key="Cusc@itmp-sm4key".toByteArray()
var pp=SM4CryptoHelper.encryptECB(key,pwd.toByteArray())
fun loginCpy() {
var pwd = "vUO2dStZDhbd*88FfT84"
var key = "Cusc@itmp-sm4key".toByteArray()
var pp = SM4CryptoHelper.encryptECB(key, pwd.toByteArray())
println("------------------pp = ${pp}")
}
@Test
fun calculateTouYing() {
// 02runTest
TestScope(UnconfinedTestDispatcher()).launch {
var gson = Gson()
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
var str =
FileIoUtils.getAsset(appContext, "mock/Car_fangzhen.txt") //Qgis里取的点和四维取得点混合
val mRoutes = gson.fromJson<MRoutes>(str, MRoutes::class.java)
// val testPoint = doubleArrayOf(116.38810256578773, 39.92848759523565) // 北海公园
val testPoint = mutableListOf<DoubleArray>()
// 坐标点串 (02坐标系)
val coordinateSeries = mutableListOf<DoubleArray>()
mRoutes.rs.forEachIndexed { index, it ->
var tLng = it[0]
var tLag = it[1]
if (index in 0..50) {
tLng = tLng + 0.00001 * index
tLag = tLag + 0.00001 * index
coordinateSeries.add(doubleArrayOf(tLng, tLag))
} else {
tLng = tLng + 0.000001 * index
tLag = tLag - 0.000001 * index
coordinateSeries.add(doubleArrayOf(tLng, tLag))
}
// coordinateSeries.add(doubleArrayOf(tLng, tLag))
testPoint.add(doubleArrayOf(it[0], it[1]))
}
// coordinateSeries.add(doubleArrayOf(116.38811674159075, 39.93087909844135))
// coordinateSeries.add(doubleArrayOf(116.38513982313117, 39.928482159906224))
// coordinateSeries.add(doubleArrayOf(116.38808130208565, 39.928291923094065))
// coordinateSeries.add(doubleArrayOf(116.39202682873122, 39.92837661180238))
// 计算投影
// double[] result = CoordinateProjectionUtils.calculatePointProjection(testPoint, coordinateSeries);
testPoint.forEachIndexed { index, it ->
val result = Proj4jCoord.calculatePointProjection(it, coordinateSeries)
println("车当前下标: (" + index + ") " + "车当前位置:" + it[0] + " " + it[1] + " 最近线段索引: " + result[2].toInt())
// println("投影点坐标: (" + result[0] + ", " + result[1] + ")")
// println("最近线段索引: " + result[2].toInt())
println("最小距离: " + result[3] + " 米")
}
}
}
}
\ No newline at end of file
......@@ -37,6 +37,7 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.SuZhouAvp"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="31">
<uses-library
......@@ -79,7 +80,7 @@
android:hardwareAccelerated="false"
android:launchMode="singleTask"
android:resizeableActivity="false"
android:screenOrientation="fullUser" />
android:screenOrientation="landscape"/>
<activity
android:name=".ui.BootActivity"
......
......@@ -32,8 +32,8 @@ class CarPanelBean {
var speed = ObservableField(37)
/***档位**/
var tapPos = ObservableField<Int>(GearStatus.D)
var gear = 0
var tapPos = ObservableField<Int>()
// var gear = ObservableField<Int>()
//剩余电量
var remainSoc = ObservableField<Int>(70)
......
......@@ -22,5 +22,5 @@ data class VehicleInfo(
val vehicleImgUrl: Any,
val vehicleOwnerName: Any,
val vehicleOwnerPhone: Any,
val vehicleVideoUrl: String
val vehicleVideoUrl: String//车内视频
)
\ No newline at end of file
......@@ -22,11 +22,14 @@ object ShowCarPanelObject {
tv.setText(speed.toString())
}
/***档位*
* **/
//档位 gear
@JvmStatic
@BindingAdapter("showTapPos")
fun showTapPos(tv: TextView, @GearStatus gear: Int) {
fun showTapPos(tv: TextView, gear: Int) {
if (gear == 7) {
tv.setTextColor("#66000000".toColorInt())
return
}
var tag = tv.tag.toString().toInt()
// println("--------tag = ${tag} gear = ${gear}")
if (tag == gear) {
......@@ -124,21 +127,20 @@ object ShowCarPanelObject {
}
/***高精地图里的车辆仪表界面***/
@JvmStatic
@BindingAdapter(value = ["showMapCarPan", "isStartNai"], requireAll = false)
fun showMapCarPan(fl: FrameLayout, isHighMap: Boolean,isStartNai: Boolean) {
fun showMapCarPan(fl: FrameLayout, isHighMap: Boolean, isStartNai: Boolean) {
// println("---------showHeading = ${heading}")
if (isHighMap){
if (isHighMap) {
// if (isStartNai){
fl.visibility= View.VISIBLE
fl.visibility = View.VISIBLE
// var params=fl.layoutParams as RelativeLayout.LayoutParams
// params.height= DisplayUtil.dp2px(106f)
// fl.layoutParams=params
// }
} else {
fl.visibility= View.GONE
fl.visibility = View.GONE
}
}
......
......@@ -56,8 +56,8 @@ object NetworkModule {
@NormalInterceptorOkHttpClient
@Provides
fun provideNormalInterceptorOkHttpClient(
headParamsInterceptor: HeadParamsInterceptor,
baseUrlInterceptor: BaseUrlInterceptor,
headParamsInterceptor: HeadParamsInterceptor
// baseUrlInterceptor: BaseUrlInterceptor,
): OkHttpClient {
// 创建信任所有证书的 TrustManager
val trustAllCerts = arrayOf<TrustManager>(
......@@ -73,7 +73,7 @@ object NetworkModule {
return OkHttpClient.Builder()
.cache(RetrofitApi.cache)
.addInterceptor(baseUrlInterceptor)
// .addInterceptor(baseUrlInterceptor)
.addInterceptor(headParamsInterceptor)
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { hostname, session -> true }
......
......@@ -17,9 +17,9 @@ class BaseUrlInterceptor @Inject constructor() : Interceptor {
var builder = original.newBuilder()
//从request中获取headers,通过给定的键url_name
val headerValue = original.header("urlname").toString()
val headerValue = original.header("urlname")
if (!headerValue.isBlank() && headerValue.count() > 0){
if (!headerValue.isNullOrEmpty() && headerValue.count()>0){
val newHttpUrl = headerValue.toHttpUrlOrNull()
val url: HttpUrl = originalHttpUrl.newBuilder()
......
......@@ -21,8 +21,14 @@ class HeadParamsInterceptor @Inject constructor() : Interceptor {
request.header("token", MyContants.HTTP_TOKEN)
request.header("Authorization", MyContants.HTTP_TOKEN)
}
// request.header("Referer", "${MyContants.HOST}/itdts-portal-v5/intelligence-parking")
request.header("Referer", "https://itg-yz.cu-sc.com:13443/itdts-portal-v5/intelligence-parking")
// request.header("Referer", "${MyContants.HOST}/itdts-portal-v5/intelligence-parking"
// var path = original.url.toUrl().path
// if (path.equals("/api/avpweb/v1/avp/overview/getVehicleInfo")) {
// request.header(
// "Referer",
// "https://itg-yz.cu-sc.com:13443/itdts-portal-v5/intelligence-parking"
// )
// }
return chain.proceed(request.build())
}
......
......@@ -23,26 +23,25 @@ interface ClientRetrofitMethod {
// fun getConfigurations(): Observable<List<ConfigurationBean>>
//
/**登录***/
@POST("api/opr/login")
@POST("api/perm/admin/auth/passLogin")
suspend fun login(@Body body: RequestBody): LoginSuccBean
/**车辆详情***/
// @POST("v1/avp/overview/getVehicleInfo")
@POST("api/avpweb/v1/avp/overview/getVehicleInfo")
suspend fun getVehDetail(@Body body: RequestBody): VehDetailBean
/**车位占用情况***/
@POST("api/avpweb/v1/avp/overview/listSpaceInfoByCondition")
suspend fun getSpaceInfo(@Body body: RequestBody): SpaceInfoBean
suspend fun getSpaceInfo(): SpaceInfoBean
/**获取已绑定车辆列表***/
@Headers("urlname:https://172.24.124.130:14443")
@POST("api/avpweb//hmi/v1/queryVehicleList")
// @Headers("urlname:https://172.24.124.130:14443")
@POST("api/avpweb/hmi/v1/queryVehicleList")
suspend fun getBindCar(): List<BindCarItem>
// suspend fun getBindCar(@Body body: RequestBody): List<BindCarItem>
/**AVP状态***/
@GET("hmi/monitor/v1/taskStatus")
@GET("api/avpweb/hmi/monitor/v1/taskStatus")
suspend fun getAvpStatus(): AvpStatuBean
......
......@@ -38,7 +38,7 @@ class AvpDataRepo @Inject constructor(private var clientRetrofitMethod: ClientRe
suspend fun getSpaceInfo(): MyResult<SpaceInfoBean> {
try {
var body = RequestBodyUtil.toRequestBody(mapOf())
var bean = clientRetrofitMethod.getSpaceInfo(body)
var bean = clientRetrofitMethod.getSpaceInfo()
return MyResult.Success(bean)
} catch (e: HttpException) {
// println("e.message = ${e.message}")
......
......@@ -32,15 +32,11 @@ class LoginActivity : AppCompatActivity() {
var key = "Cusc@itmp-sm4key".toByteArray()
var pp = SM4CryptoHelper.encryptECB(key, pwd.toByteArray())
loginVm.login(user, pp, 276135).observe(this) {
loginVm.login(user, pp, 285369).observe(this) {
// startActivity(Intent(this,MainActivity::class.java))
}
}
// HTTP获取车辆详情
binding.btVehinfo.setOnClickListener {
mainVm.getVehDetail()
}
// HTTP获取车位占用情况
binding.btSpaceinfo.setOnClickListener {
mainVm.getSpaceInfo()
......@@ -49,6 +45,7 @@ class LoginActivity : AppCompatActivity() {
binding.btAvpstatu.setOnClickListener {
mainVm.getAvpStatus()
}
//获取可绑定车辆
binding.btGetbindcar.setOnClickListener {
getBinderCars()
}
......@@ -62,16 +59,19 @@ class LoginActivity : AppCompatActivity() {
private fun getBinderCars() {
mainVm.getBindCar().observe(this) {
var list = it.map { it.id.toString() }
val dialog = CustomListDialog(this, "选择车辆", list)
dialog.setOnItemClickListener(object : CustomListDialog.OnItemClickListener {
override fun onItemClick(position: Int, selectedItem: String) {
Toast.makeText(
this@LoginActivity,
"点击了第${position + 1}项: $selectedItem",
Toast.LENGTH_SHORT
).show()
}
})
dialog.show()
}
// var list = listOf("skywell.1ggvlp16.car10", "skywell.1ggvlp16.car8")
// val dialog = CustomListDialog(this, "选择车辆", list)
// dialog.setOnItemClickListener(object : CustomListDialog.OnItemClickListener {
// override fun onItemClick(position: Int, selectedItem: String) {
// Toast.makeText(this@LoginActivity, "点击了第${position + 1}项: $selectedItem", Toast.LENGTH_SHORT).show()
// }
// })
// dialog.show()
}
......
package com.sd.cavphmi.ui
import android.view.KeyEvent
import android.widget.RelativeLayout
import androidx.activity.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
......@@ -75,6 +74,7 @@ class MainActivity : BaseActivity<ActivityMainBinding, MyBaseViewModel>() {
//用作模拟
private val mockVM: MockVM by viewModels()
//主页操作
private val mainVm: MainVm by viewModels()
//地图操作类,用于绘制
......@@ -100,34 +100,30 @@ class MainActivity : BaseActivity<ActivityMainBinding, MyBaseViewModel>() {
ft.add(R.id.map_car_pan, carPanelFragment, "1")
ft.commit()
//设置视频播放器长和宽
var height = DisplayUtil.getScreenHeightPx().div(3) - 20
var width = height.times(2) - 30
var params = binding.videoFrag.layoutParams as RelativeLayout.LayoutParams
params.width = width
params.height = height
binding.videoFrag.layoutParams = params
//添加视频播放器,以后看需要控制显示隐藏时机,默认隐藏
var ft2 = supportFragmentManager.beginTransaction()
ft2.add(R.id.video_frag, exoPlayFragment, "player")
ft2.hide(exoPlayFragment)
ft2.commit()
showVideoFragment(true)
initialMap()
}
private fun adaptWidth() {
// DisplayUtil.forceMeasure(binding.root)
//// var height = 0
// var width = binding.root.measuredWidth
// var cWidth = (width * 0.27).toInt()
////车辆仪表
// var params = binding.mapCarPan.layoutParams.apply {
// width = cWidth
// }
// binding.mapCarPan.layoutParams = params
var width = DisplayUtil.getScreenWidthPx()
var cWidth = (width * 0.271).toInt()
//车辆仪表
var params = binding.mapCarPan.layoutParams
params.width = cWidth
binding.mapCarPan.layoutParams = params
//小地图
params = binding.smallFLayout.layoutParams.apply {
this.width = (width * 0.17).toInt()
this.height = (this.width * 0.75).toInt()
}
binding.smallFLayout.layoutParams = params
//车内视频frag
params = binding.videoFrag.layoutParams.apply {
this.width = (width * 0.314).toInt()
this.height = (this.width * 0.54).toInt()
}
binding.videoFrag.layoutParams = params
}
private fun initialMap() {
......@@ -164,6 +160,24 @@ class MainActivity : BaseActivity<ActivityMainBinding, MyBaseViewModel>() {
override fun getToData() {
//开启websocket
// mainVm.startWS()
//获取车位占用情况
mainVm.getSpaceInfo().observe(this) { spaceInfo ->
}
}
//显示隐藏 视频
private fun showVideoFragment(show: Boolean) {
var ft = supportFragmentManager.beginTransaction()
if (!exoPlayFragment.isAdded) {
ft.add(R.id.video_frag, exoPlayFragment, "player")
}
if (show) {
ft.show(exoPlayFragment)
} else {
ft.hide(exoPlayFragment)
}
ft.commit()
}
override fun initListener() {
......@@ -175,6 +189,7 @@ class MainActivity : BaseActivity<ActivityMainBinding, MyBaseViewModel>() {
mainVm.getAvpStatus().observe(this) { avpStatu ->
//业务类型
var businessType = avpStatu.businessType//NIL Park Call
//业务状态
var businessStatus =
avpStatu.businessStatus//NIL WAITING PROCESSING COMPLETED CANCELLED FAILED
//获取档位
......@@ -215,7 +230,7 @@ class MainActivity : BaseActivity<ActivityMainBinding, MyBaseViewModel>() {
}
}
//联网车辆感知物
private fun getTarget() {
mainVm.subTarget().observe(this) {
if (it.isEmpty()) {
......@@ -230,9 +245,10 @@ class MainActivity : BaseActivity<ActivityMainBinding, MyBaseViewModel>() {
lon = it.longitude
ptcid = it.ptcId
heading = it.heading
pType = 1
if (it.ptcType == "car") {
pType = 1
} else {
} else if (it.ptcType == "pedestrian") {
pType = 2
}
}
......@@ -247,14 +263,25 @@ class MainActivity : BaseActivity<ActivityMainBinding, MyBaseViewModel>() {
mainVm.subStartV2x().observe(this) { v2x ->
if (v2x.objects?.isEmpty() == true)
return@observe
//获取预警感知目标物的id 第二个是
//获取预警感知目标物的id 第一个是自己 第二个是别人
var v2xId = v2x.objects!!.get(1).id
v2xId = "f117fdfa-feff-0100-85dc-35850000acb0"
// v2xId = "f117fdfa-feff-0100-85dc-35850000acb0"
mainVm.startWarning(v2xId)
}
}
//获取车辆详情,取车内摄像头地址打开左下角的车内视频
private fun getVehDetail() {
mainVm.getVehDetail().observe(this) { vehDetail ->
var cameraUrl = vehDetail.result.vehicleInfos?.get(0)?.vehicleVideoUrl
println("---cameraUrl = ${cameraUrl}")
if (!cameraUrl.isNullOrEmpty()) {
exoPlayFragment.videoUrl = cameraUrl
showVideoFragment(true)
}
}
}
private fun mockBt() {
//获取AVP状态
binding.btAvpStatu.setOnClickListener {
......@@ -268,6 +295,10 @@ class MainActivity : BaseActivity<ActivityMainBinding, MyBaseViewModel>() {
binding.btTarget.setOnClickListener {
getTarget()
}
// HTTP获取车辆详情
binding.btVehinfo.setOnClickListener {
getVehDetail()
}
//v2x 预警
binding.btV2x.setOnClickListener {
getV2x()
......
......@@ -48,15 +48,15 @@ class CarPanelFragment : BaseFragment<FragmentCarPanelBinding, MyBaseViewModel>(
mainVm.carVehicle.observe(this) {
carPanelVM.setCarPanelBean(it)
}
mainVm.avpStatu.observe(this) {
var gearType = it.vehicleContext.vehicleDynamic.gearType
carPanelVM.setGearType(gearType)
}
// mainVm.avpStatu.observe(this) {
// var gearType = it.vehicleContext.vehicleDynamic.gearType
// carPanelVM.setGearType(gearType)
// }
}
override fun getToData() {
// carPanelVM.mock()
}
companion object {
......
package com.sd.cavphmi.ui.fragment
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.toColorInt
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import com.sd.cavphmi.bean.AvpStatuBean
import com.sd.cavphmi.databinding.FragmentDistantTipBinding
import com.sd.cavphmi.utils.MyMapUtils
import com.sd.cavphmi.viewmodels.MainVm
import kotlinx.coroutines.launch
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
*距离提示
*/
class DistantTipFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentDistantTipBinding.inflate(inflater, container, false)
return binding.root
// Inflate the layout for this fragment
// return inflater.inflate(R.layout.fragment_distant_tip, container, false)
}
private lateinit var binding: FragmentDistantTipBinding
//主页操作
private val mainVm: MainVm by activityViewModels()
private val spannableString = SpannableStringBuilder()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mainVm.avpStatu.observe(viewLifecycleOwner) { avpStatu ->
showTip(avpStatu)
}
}
private fun showTip(avpStatu: AvpStatuBean) {
//业务类型
var businessType = avpStatu.businessType//NIL Park Call
//业务状态
var businessStatus =
avpStatu.businessStatus//NIL WAITING PROCESSING COMPLETED CANCELLED FAILED
lifecycleScope.launch {
val vehicleDynamic = avpStatu.vehicleContext.vehicleDynamic
val endPoint = avpStatu.drivenDecision.trajectory.endPoint
//计算车到终点的距离
var distance = MyMapUtils.cauMyLocDistance(
vehicleDynamic.latitude,
vehicleDynamic.longitude,
endPoint.latitude,
endPoint.longitude
)
if (businessType == "Park") {
when (businessStatus) {
// "WAITING" -> {
// binding.tvTip.text = "正在前往您的车位,距离终点${distance}m"
// }
"PROCESSING" -> {
takeStr(distance, 1)
}
"COMPLETED" -> {
clearTipStr()
}
}
} else if (businessType == "Call") {
when (businessStatus) {
// "WAITING" -> {
// binding.tvTip.text = "正在前往您召车点,距离乘客${distance}m"
// }
"PROCESSING" -> {
takeStr(distance, 2)
}
"COMPLETED" -> {
clearTipStr()
}
}
}
}
}
/**
* 生成提示字符串
* @param x 1=泊车 2=招车
*/
private fun takeStr(distance: Double, x: Int) {
if (distance < 3) {
clearTipStr()
return
}
if (x == 1) {
spannableString.append("正在前往您的车位,距离终点${distance}m")
} else if (x == 2) {
spannableString.append("正在前往您召车点,距离乘客${distance}m")
}
var colorSpan1 = ForegroundColorSpan("#000000".toColorInt())
spannableString.setSpan(
colorSpan1,
0, 12, // 起始索引
Spannable.SPAN_INCLUSIVE_INCLUSIVE // 范围模式
)
var colorSpan2 = ForegroundColorSpan("#3385FE".toColorInt())
spannableString.setSpan(
colorSpan2,
13, spannableString.count(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
)
binding.tvTip.text = spannableString
}
//清除提示字符串
private fun clearTipStr() {
spannableString.clear()
binding.tvTip.text = ""
}
private fun testStr() {
val text = "正在前往您的车位,距离终点6m"
val spannableString = SpannableStringBuilder().append(text)
// val spannableString = SpannableString(text)
var colorSpan1 = ForegroundColorSpan("#000000".toColorInt())
spannableString.setSpan(
colorSpan1,
0, 12, // 起始索引(包含)、结束索引(不包含)
Spannable.SPAN_INCLUSIVE_INCLUSIVE // 范围模式(见下文说明)
)
var colorSpan2 = ForegroundColorSpan("#3385FE".toColorInt())
spannableString.setSpan(
colorSpan2,
13, spannableString.count(), // 起始索引(包含)、结束索引(不包含)
Spannable.SPAN_INCLUSIVE_EXCLUSIVE // 范围模式(见下文说明)
)
binding.tvTip.text = spannableString
}
companion object {
@JvmStatic
fun newInstance() = DistantTipFragment()
}
}
\ No newline at end of file
......@@ -15,20 +15,20 @@ import com.sd.cavphmi.databinding.FragmentExoPlayBinding
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val VIDEO_PARAM1 = "video"
private const val VIDEO_URL = "videoUrl"
private const val ARG_PARAM2 = "param2"
/**
*车内视频播放器
*/
class ExoPlayFragment : BaseFragment<FragmentExoPlayBinding, MyBaseViewModel>() {
private var videoUrl: String = ""
// private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
videoUrl = it.getString(VIDEO_PARAM1, "")
videoUrl = it.getString(VIDEO_URL, "")
// param2 = it.getString(ARG_PARAM2)
}
}
......@@ -64,40 +64,53 @@ class ExoPlayFragment : BaseFragment<FragmentExoPlayBinding, MyBaseViewModel>()
binding.playerView.onPause()
}
override fun onStop() {
super.onStop()
binding.playerView.onPause()
releasePlayer()
}
override fun onDestroy() {
super.onDestroy()
binding.playerView.getAdViewGroup().removeAllViews()
// binding.playerView.getAdViewGroup().removeAllViews()
releasePlayer()
}
private lateinit var player: ExoPlayer
private var player: ExoPlayer? = null
// private var videoUrl = "http://172.24.124.88/yizhuang/2d83a14117e54d3b981f7d50bdc42c47.live.flv"
var videoUrl =
"https://itg-yz.cu-sc.com:13443/video/yizhuang/0b130981fd754f7e817797235f399264.live.flv"
//用于测试,实际可以传参进来
var videoUri = "https://faw.cuscavp.cn:8443/hdl/live/3.flv"
private var videoUri = "https://faw.cuscavp.cn:8443/hdl/live/3.flv"
override fun initView() {
// DisplayUtil.forceMeasure(binding.playerView)
// 重要:必须使用 texture_view 才能显示圆角
binding.playerView.apply {
// setShutterBackgroundColor(Color.TRANSPARENT)
// clipToOutline = true
// clipChildren=true
// outlineProvider = object : ViewOutlineProvider() {
// override fun getOutline(view: View, outline: Outline) {
// outline.setRoundRect(10, 200,400, 800, 40f)
// }
// }
// setBackgroundResource(R.drawable.rect_no_col_10)
}
initializePlayer()
}
fun initializePlayer() {
if (player == null) {
val playerBuilder =
ExoPlayer.Builder(requireContext())
.setMediaSourceFactory(createMediaSourceFactory())
player = playerBuilder.build()
player.addListener(playerEventListener)
player.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true);
player.setPlayWhenReady(true)
player!!.addListener(playerEventListener)
player?.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true);
player?.setPlayWhenReady(true)
binding.playerView.setPlayer(player)
// Build the media item.
val mediaItem = MediaItem.Builder()
.setUri(videoUri)
.setUri(videoUrl)
.setLiveConfiguration(
MediaItem.LiveConfiguration.Builder()
.setMaxPlaybackSpeed(1.02f)
......@@ -106,22 +119,50 @@ class ExoPlayFragment : BaseFragment<FragmentExoPlayBinding, MyBaseViewModel>()
)
.build()
player.setMediaItem(mediaItem)
player.prepare()
player?.setMediaItem(mediaItem)
player?.prepare()
}
// Start the playback.
// player.play()
}
fun releasePlayer() {
player.release()
binding.playerView.setPlayer(/* player= */ null);
binding.playerView.setPlayer(/* player= */ null)
player?.release()
player = null
}
private var playerEventListener=object : Player.Listener{
private var playerEventListener = object : Player.Listener {
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
println("------ExoPlayFragment.onPlayerError = ${error.message}")
}
override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState)
when (playbackState) {
Player.STATE_READY -> {
// 播放器准备好时隐藏占位图
// placeholderImage.visibility = View.GONE
}
Player.STATE_BUFFERING, Player.STATE_IDLE -> {
// 缓冲或空闲时显示占位图
// placeholderImage.visibility = View.VISIBLE
}
}
}
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
super.onPlayWhenReadyChanged(playWhenReady, reason)
println("------ExoPlayFragment playWhenReady = ${playWhenReady}")
}
override fun onRenderedFirstFrame() {
super.onRenderedFirstFrame()
println("------ExoPlayFragment.onRenderedFirstFrame")
}
}
......@@ -129,14 +170,12 @@ class ExoPlayFragment : BaseFragment<FragmentExoPlayBinding, MyBaseViewModel>()
companion object {
@JvmStatic
fun newInstance() = ExoPlayFragment()
/*
@JvmStatic
fun newInstance(video: String) =
ExoPlayFragment().apply {
arguments = Bundle().apply {
putString(VIDEO_PARAM1, video)
}
}
*/
// @JvmStatic
// fun newInstance(videoUrl: String) =
// ExoPlayFragment().apply {
// arguments = Bundle().apply {
// putString(VIDEO_URL, videoUrl)
// }
// }
}
}
\ No newline at end of file
......@@ -83,6 +83,7 @@ class WarnFragment : BaseFragment<FragmentWarnBinding, MyBaseViewModel>() {
}
}
//显示预警气泡
private fun showEarlyDetail(v2xStartBean: V2xStartBean) {
var warningBean = WarningBean.instance
warningBean.img = R.drawable.chao_su
......
package com.sd.cavphmi.ui.view
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
import androidx.media3.ui.PlayerView
class RoundedPlayerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : PlayerView(context, attrs, defStyleAttr) {
private val path = Path()
private val rect = RectF()
private val radius = 16f.dpToPx(context) // 圆角半径(转换为像素)
init {
// 对于 ExoPlayer 3,需要关闭硬件加速或使用其他方式
// setLayerType(LAYER_TYPE_SOFTWARE, null)
// updateOutline()
}
// override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec)
// // 可以在这里根据圆角调整测量逻辑
// val width = MeasureSpec.getSize(MeasureSpec.AT_MOST)
// val height = MeasureSpec.getSize(MeasureSpec.AT_MOST)
//
// setMeasuredDimension(width, height)
//
// updateOutline()
// }
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
// updateOutline()
}
private fun updateOutline() {
// 启用轮廓裁剪
clipToOutline = true
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setRoundRect(0, 0, view.width, view.height, radius)
}
}
}
// dp 转 px 工具方法
private fun Float.dpToPx(context: Context): Float {
return this * context.resources.displayMetrics.density
}
}
......@@ -78,7 +78,7 @@ object FileIoUtils {
/**
* 读取asset 文件
*/
suspend fun getAsset(context: Context, fileName: String): String {
fun getAsset(context: Context, fileName: String): String {
val assetManager = context.assets
var inputStream: InputStream? = null
var str = ""
......
......@@ -8,11 +8,9 @@ object MyContants {
//测试环境
// var HOST = "https://itg-dev.cu-sc.com:19443/"
//开发环境切勿动
var HOST = "https://itg-yz.cu-sc.com:13443"
//HTTP地址
var HOST = "https://172.24.124.130:19443"
//智网生产环境地址用于拼接3dtile.json
var HOST_HTTP_3 = "https://itg-yz.cu-sc.com:13443"
// /***测试环境socket token***/
// private val WSTOKEN =
// "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiI0MTc0NDY3NGNlOGM0MDZmOTVkZTVkYWYyMWVlOWQ0ZiIsImNyZWF0ZVRpbWUiOjE3NTUwNzYxMTgxMjQsInVzZXJUeXBlIjoxLCJzb3VyY2UiOjAsInB3ZEV4cGlyZWQiOmZhbHNlLCJ1c2VybmFtZSI6ImNoZW5ieTUxIn0.aPYHCxXgQHj4eYGGZnce5MPJCtmMoRcIIHcNXzMMOHE"
......@@ -26,26 +24,27 @@ object MyContants {
//跟踪车辆的ID 15(模拟) skywell.1ggvlp16.car10 skywell.1ggvlp16.car8
//用来拼接websocket
var BASE_HOST =
if (IS_DEBUG) "172.24.124.130:19443" else "itg-yz.cu-sc.com:19443"
var BASE_HOST = "172.24.124.130:19443"
// if (IS_DEBUG) "itg-dev.cu-sc.com:19443" else "itg-yz.cu-sc.com:19443"
// if (IS_DEBUG) "172.24.124.130:19443" else "itg-yz.cu-sc.com:19443"
// if (IS_DEBUG) "itg-yz.cu-sc.com:13443" else "itg-yz.cu-sc.com:19443"
//websocket地址
private var WSHOST =
if (IS_DEBUG) "wss://${BASE_HOST}/wsplus/socket?token=121&reType=freedo&" else "wss://${BASE_HOST}/wsplus/socket?token=121&reType=freedo&"
private var WSHOST = "wss://${BASE_HOST}/wsplus/socket?token=121&reType=freedo&"
// if (IS_DEBUG) "wss://${BASE_HOST}/wsplus/socket?token=121&reType=freedo&" else "wss://${BASE_HOST}/wsplus/socket?token=121&reType=freedo&"
//车辆编号
var VEHICLEID = "YZMN003"
//车辆编号 YZMN003
var VEHICLEID = "15"
/**网联车辆位姿 用于实车测试喽数据***/
/**网联车辆位姿 用于实车测试喽数据 &vehicleId=${VEHICLEID}***/
var WS_VEH_LOC = "${WSHOST}msgType=2&vehicleId=${VEHICLEID}"
/***感知目标物 &intersectionCode=17 停车场 不传就是整个园区***/
// var WS_FEEL_TARGET = "${WSHOST}msgType=1&intersectionCode=17"
var WS_FEEL_TARGET = "${WSHOST}&msgType=1"
/***V2X预警 VEHICLEID 传了就是获取某一辆车的预警 &VEHICLEID=***/
var WS_V2X = "${WSHOST}msgType=4"
/***V2X预警 VEHICLEID 传了就是获取某一辆车的预警 &vehicleId=${VEHICLEID}"=***/
var WS_V2X = "${WSHOST}msgType=4&vehicleId=${VEHICLEID}"
/***网联车辆状态 &VEHICLEID=''***/
var WS_VEH_STATU = "${WSHOST}msgType=6"
......@@ -59,7 +58,6 @@ object MyContants {
/***交通信号灯 &intersectionCode=''***/
var WS_TRAFFIC_LIGHT = "${WSHOST}msgType=3&"
/***已经**/
const val ALREADT_ONCE = -1
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment