添加版本更新弹窗
This commit is contained in:
parent
3143f4007e
commit
9e7d8ebb48
@ -56,6 +56,7 @@ import com.zs.smarthuman.utils.DangerousUtils
|
|||||||
import com.zs.smarthuman.utils.UnityPlayerHolder
|
import com.zs.smarthuman.utils.UnityPlayerHolder
|
||||||
import com.zs.smarthuman.utils.ViewSlideAnimator
|
import com.zs.smarthuman.utils.ViewSlideAnimator
|
||||||
import com.zs.smarthuman.viewmodel.MainViewModel
|
import com.zs.smarthuman.viewmodel.MainViewModel
|
||||||
|
import com.zs.smarthuman.widget.VersionUpdateDialog
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@ -93,6 +94,8 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
private val audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
private val audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
||||||
private var mVerticalAnimator: ViewSlideAnimator? = null
|
private var mVerticalAnimator: ViewSlideAnimator? = null
|
||||||
|
|
||||||
|
private var versionUpdateDialog: VersionUpdateDialog? = null
|
||||||
|
|
||||||
override fun getViewBinding(): ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater)
|
override fun getViewBinding(): ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
UnityPlayerHolder.getInstance().initialize(this)
|
UnityPlayerHolder.getInstance().initialize(this)
|
||||||
@ -101,6 +104,7 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
.setDirection(ViewSlideAnimator.Direction.VERTICAL)
|
.setDirection(ViewSlideAnimator.Direction.VERTICAL)
|
||||||
.setAnimationDuration(300)
|
.setAnimationDuration(300)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initData() {
|
override fun initData() {
|
||||||
@ -131,7 +135,7 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
mViewModel?.getUserInfo()
|
mViewModel?.getUserInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun versionUpdate(){
|
private fun versionUpdate() {
|
||||||
mViewModel?.versionUpdate()
|
mViewModel?.versionUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,13 +168,16 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
mViewModel?.versionUpdateLiveData?.observe(this){
|
mViewModel?.versionUpdateLiveData?.observe(this) {
|
||||||
when(it){
|
when (it) {
|
||||||
is ApiResult.Error -> {}
|
is ApiResult.Error -> {}
|
||||||
is ApiResult.Success<VersionUpdateResp> -> {
|
is ApiResult.Success<VersionUpdateResp> -> {
|
||||||
if (it.data.versionCode > BuildConfig.VERSION_CODE)
|
if (it.data.versionCode > BuildConfig.VERSION_CODE) {//有新版本
|
||||||
{//有新版本
|
versionUpdateDialog = VersionUpdateDialog(this, it.data)
|
||||||
|
versionUpdateDialog?.show()
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
downloadApk(it.data.downloadUrl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,7 +197,8 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
voiceInfo = mutableListOf<VoiceBeanResp>().apply {
|
voiceInfo = mutableListOf<VoiceBeanResp>().apply {
|
||||||
add(
|
add(
|
||||||
VoiceBeanResp(
|
VoiceBeanResp(
|
||||||
audioUrl = UserInfoManager.userInfo?.wakeUpAudioUrl ?: "https://static.seerteach.net/aidialogue/systemVoice/aliyun-nv.mp3"
|
audioUrl = UserInfoManager.userInfo?.wakeUpAudioUrl
|
||||||
|
?: "https://static.seerteach.net/aidialogue/systemVoice/aliyun-nv.mp3"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -209,7 +217,7 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
)
|
)
|
||||||
AudioDebugUtil.saveFloatPcmAsWav(audio, file)
|
AudioDebugUtil.saveFloatPcmAsWav(audio, file)
|
||||||
LogUtils.dTag("audioxx", "WAV saved: ${file.path}, samples=${audio.size}")
|
LogUtils.dTag("audioxx", "WAV saved: ${file.path}, samples=${audio.size}")
|
||||||
lifecycleScope.launch(Dispatchers.Main){
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
|
||||||
mVerticalAnimator?.show()
|
mVerticalAnimator?.show()
|
||||||
}
|
}
|
||||||
@ -235,11 +243,13 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
VoiceState.WAIT_SPEECH -> {
|
VoiceState.WAIT_SPEECH -> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VoiceState.RECORDING -> {
|
VoiceState.RECORDING -> {
|
||||||
startRecording()
|
startRecording()
|
||||||
}
|
}
|
||||||
VoiceState.PLAYING_PROMPT ->{}
|
|
||||||
VoiceState.PLAYING_BACKEND ->{}
|
VoiceState.PLAYING_PROMPT -> {}
|
||||||
|
VoiceState.PLAYING_BACKEND -> {}
|
||||||
VoiceState.UPLOADING -> {}
|
VoiceState.UPLOADING -> {}
|
||||||
|
|
||||||
VoiceState.WAIT_SPEECH_COOLDOWN -> {}
|
VoiceState.WAIT_SPEECH_COOLDOWN -> {}
|
||||||
@ -340,7 +350,11 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
if (isRecording) return
|
if (isRecording) return
|
||||||
if (audioRecord == null && !initMicrophone()) return
|
if (audioRecord == null && !initMicrophone()) return
|
||||||
|
|
||||||
try { audioRecord?.startRecording() } catch (e: IllegalStateException) { recreateAudioRecord(); return }
|
try {
|
||||||
|
audioRecord?.startRecording()
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
recreateAudioRecord(); return
|
||||||
|
}
|
||||||
isRecording = true
|
isRecording = true
|
||||||
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
@ -359,7 +373,6 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun recreateAudioRecord() {
|
private fun recreateAudioRecord() {
|
||||||
stopRecording()
|
stopRecording()
|
||||||
try {
|
try {
|
||||||
@ -403,8 +416,8 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
word: String,
|
word: String,
|
||||||
audioUrl: String
|
audioUrl: String
|
||||||
) {
|
) {
|
||||||
val wakeupUrl = UserInfoManager.userInfo?.wakeUpAudioUrl ?:
|
val wakeupUrl = UserInfoManager.userInfo?.wakeUpAudioUrl
|
||||||
"https://static.seerteach.net/aidialogue/systemVoice/aliyun-nv.mp3"
|
?: "https://static.seerteach.net/aidialogue/systemVoice/aliyun-nv.mp3"
|
||||||
|
|
||||||
if (audioUrl != wakeupUrl) return
|
if (audioUrl != wakeupUrl) return
|
||||||
|
|
||||||
@ -487,24 +500,24 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
|||||||
.toDownloadFlow(destPath)
|
.toDownloadFlow(destPath)
|
||||||
.onProgress {
|
.onProgress {
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
versionUpdateDialog?.updateProgress(it.progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
}.catch {
|
}.catch {
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
versionUpdateDialog?.dismiss()
|
||||||
}
|
}
|
||||||
}.collect {
|
}.collect {
|
||||||
//下载完成
|
//下载完成
|
||||||
if (AppUtils.isAppRoot()) {
|
if (AppUtils.isAppRoot()) {
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
DangerousUtils.installAppSilent(it, "-r")
|
DangerousUtils.installAppSilent(it, "-r")
|
||||||
|
versionUpdateDialog?.dismiss()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
AppUtils.installApp(it)
|
AppUtils.installApp(it)
|
||||||
|
versionUpdateDialog?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
65
app/src/main/java/com/zs/smarthuman/widget/BaseDialog.kt
Normal file
65
app/src/main/java/com/zs/smarthuman/widget/BaseDialog.kt
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package com.zs.smarthuman.widget
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.zs.smarthuman.R
|
||||||
|
import kotlin.let
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lrs
|
||||||
|
* @date 2025/4/12 19:35
|
||||||
|
* @description 基础dialog
|
||||||
|
*/
|
||||||
|
abstract class BaseDialog<T : ViewBinding>(context: Context) : Dialog(context,R.style.LightDialog) {
|
||||||
|
lateinit var binding: T
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = getViewBinding()
|
||||||
|
setContentView(binding.root)
|
||||||
|
initView()
|
||||||
|
}
|
||||||
|
protected abstract fun getViewBinding(): T
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
this.window?.setFlags(
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||||
|
)
|
||||||
|
super.show()
|
||||||
|
window?.let {
|
||||||
|
fullScreenImmersive(it.decorView)
|
||||||
|
it.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
|
||||||
|
it.setDimAmount(0.85f)
|
||||||
|
}
|
||||||
|
//设置dialog弹窗宽高
|
||||||
|
val params: WindowManager.LayoutParams = window!!.attributes
|
||||||
|
params.height = ConstraintLayout.LayoutParams.MATCH_PARENT
|
||||||
|
params.width = ConstraintLayout.LayoutParams.MATCH_PARENT
|
||||||
|
window?.attributes = params
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("ObsoleteSdkInt")
|
||||||
|
private fun fullScreenImmersive(view: View) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
|
val uiOptions = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||||
|
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
|
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||||
|
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
|
or View.SYSTEM_UI_FLAG_FULLSCREEN)
|
||||||
|
view.systemUiVisibility = uiOptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun initView()
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.zs.smarthuman.widget
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.zs.smarthuman.bean.VersionUpdateResp
|
||||||
|
import com.zs.smarthuman.databinding.DialogVersionUpdateBinding
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description:
|
||||||
|
* @author: lrs
|
||||||
|
* @date: 2026/1/5 9:41
|
||||||
|
*/
|
||||||
|
class VersionUpdateDialog(context: Context, val versionUpdateResp: VersionUpdateResp) :
|
||||||
|
BaseDialog<DialogVersionUpdateBinding>(context) {
|
||||||
|
override fun getViewBinding(): DialogVersionUpdateBinding =
|
||||||
|
DialogVersionUpdateBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
binding.tvDesc.text = versionUpdateResp.updateLog
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateProgress(current: Int){
|
||||||
|
binding.pb.progress = current
|
||||||
|
}
|
||||||
|
}
|
||||||
9
app/src/main/res/drawable/bg_version_update_dialog.xml
Normal file
9
app/src/main/res/drawable/bg_version_update_dialog.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
<gradient
|
||||||
|
android:angle="270"
|
||||||
|
android:endColor="#E0CBFE"
|
||||||
|
android:startColor="#FFFFFF" />
|
||||||
|
</shape>
|
||||||
BIN
app/src/main/res/drawable/icon_rocket.png
Normal file
BIN
app/src/main/res/drawable/icon_rocket.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
23
app/src/main/res/drawable/version_update_progress.xml
Normal file
23
app/src/main/res/drawable/version_update_progress.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- 设置背景色(蓝色) -->
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="@color/white" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item android:id="@android:id/progress">
|
||||||
|
<clip>
|
||||||
|
<shape>
|
||||||
|
<gradient
|
||||||
|
android:endColor="#DCA5FE"
|
||||||
|
android:startColor="#7E7DFC" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
</shape>
|
||||||
|
</clip>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</layer-list>
|
||||||
96
app/src/main/res/layout/dialog_version_update.xml
Normal file
96
app/src/main/res/layout/dialog_version_update.xml
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/view_content_layout"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:background="@drawable/bg_version_update_dialog"
|
||||||
|
app:layout_constraintDimensionRatio="H,284:321"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/iv_rocket"
|
||||||
|
app:layout_constraintWidth_percent="0.75" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_rocket"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="@drawable/icon_rocket"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="H,64:109"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.2"
|
||||||
|
app:layout_constraintWidth_percent="0.17" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="版本更新中"
|
||||||
|
android:textColor="#333333"
|
||||||
|
android:textSize="20sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/view_content_layout"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/view_content_layout"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/iv_rocket" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_second_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="25dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="新版本更新内容如下:"
|
||||||
|
android:textColor="#333333"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/view_content_layout"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tv_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_desc"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginHorizontal="25dp"
|
||||||
|
android:layout_marginVertical="3dp"
|
||||||
|
android:text="优化内容,修复bug"
|
||||||
|
android:textColor="#4C4C4C"
|
||||||
|
android:textSize="10sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/pb"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/view_content_layout"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/view_content_layout"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tv_second_title" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/pb"
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:max="100"
|
||||||
|
android:progress="50"
|
||||||
|
android:progressDrawable="@drawable/version_update_progress"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/tv_bottom_desc"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/view_content_layout"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/view_content_layout"
|
||||||
|
app:layout_constraintWidth_percent="0.5" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_bottom_desc"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
android:text="请耐心等候..."
|
||||||
|
android:textColor="#A3A3A3"
|
||||||
|
android:textSize="10sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/view_content_layout"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/view_content_layout"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/view_content_layout" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@ -17,4 +17,15 @@
|
|||||||
<item name="android:windowIsTranslucent">true</item>
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
<item name="android:backgroundDimEnabled">false</item>
|
<item name="android:backgroundDimEnabled">false</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="LightDialog" parent="@android:style/Theme.Dialog">
|
||||||
|
<item name="android:windowFrame">@null</item >
|
||||||
|
<item name="android:windowIsFloating">true</item>
|
||||||
|
<item name="android:windowContentOverlay">@null</item>
|
||||||
|
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
|
||||||
|
<item name="android:windowSoftInputMode">stateUnspecified|adjustPan
|
||||||
|
</item>
|
||||||
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
|
<item name="android:windowNoTitle">true</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
x
Reference in New Issue
Block a user