Commit f11596b8 authored by yinjiacheng's avatar yinjiacheng

update 个人认证流程

parent 244ff136
...@@ -21,4 +21,6 @@ class LifeAccountPersonalAuthData { ...@@ -21,4 +21,6 @@ class LifeAccountPersonalAuthData {
var validDateStart: String = "" var validDateStart: String = ""
var validDateEnd: String = "" var validDateEnd: String = ""
var issuedBy: String = "" var issuedBy: String = ""
var isFaceAuthPass: Boolean = false
var isIDCardAuthPass: Boolean = false
} }
...@@ -37,6 +37,8 @@ import com.yidian.shenghuoquan.newscontent.ui.dialog.BottomSelectDialog ...@@ -37,6 +37,8 @@ import com.yidian.shenghuoquan.newscontent.ui.dialog.BottomSelectDialog
import com.yidian.shenghuoquan.newscontent.utils.BitmapUtil import com.yidian.shenghuoquan.newscontent.utils.BitmapUtil
import com.yidian.shenghuoquan.newscontent.utils.FileUtil import com.yidian.shenghuoquan.newscontent.utils.FileUtil
import com.yidian.shenghuoquan.newscontent.utils.KS3Core import com.yidian.shenghuoquan.newscontent.utils.KS3Core
import com.yidian.shenghuoquan.newscontent.widget.LifeAccountAuthIdentityInfoEditView
import com.yidian.shenghuoquan.newscontent.widget.LifeAccountAuthImageView
import com.yidian.utils.ToastUtil import com.yidian.utils.ToastUtil
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
...@@ -50,7 +52,8 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -50,7 +52,8 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
View.OnClickListener, BottomSelectAdapter.OnItemClickListener<BottomSelectBean>, View.OnClickListener, BottomSelectAdapter.OnItemClickListener<BottomSelectBean>,
IGetIDCardOCRCallback, AuthPersonalGetTokenCallback, IdentifyIdOcrVerifyCallback, PreCallback, IGetIDCardOCRCallback, AuthPersonalGetTokenCallback, IdentifyIdOcrVerifyCallback, PreCallback,
DetectCallback, KS3Core.OnKS3TaskListener, IAuthPersonalCheckCallback, DetectCallback, KS3Core.OnKS3TaskListener, IAuthPersonalCheckCallback,
IAuthLiveIdentityCallback { IAuthLiveIdentityCallback, LifeAccountAuthImageView.OnLifeAccountAuthImageViewCallback,
LifeAccountAuthIdentityInfoEditView.OnLifeAccountAuthIdentityInfoEditViewCallback {
companion object { companion object {
// 身份证采集页面回传数据 // 身份证采集页面回传数据
...@@ -108,6 +111,10 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -108,6 +111,10 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
viewBinding.ivIdCardPortraitFace.setOnClickListener(this) viewBinding.ivIdCardPortraitFace.setOnClickListener(this)
viewBinding.ivIdCardNationalEmblemFace.setOnClickListener(this) viewBinding.ivIdCardNationalEmblemFace.setOnClickListener(this)
viewBinding.ivFaceAuthStart.setOnClickListener(this) viewBinding.ivFaceAuthStart.setOnClickListener(this)
viewBinding.ivIdCardPortraitFace.setOnLifeAccountAuthImageViewCallback(this)
viewBinding.ivIdCardNationalEmblemFace.setOnLifeAccountAuthImageViewCallback(this)
viewBinding.evRealName.setOnLifeAccountAuthIdentityInfoEditViewCallback(this)
viewBinding.evIdCardNumber.setOnLifeAccountAuthIdentityInfoEditViewCallback(this)
} }
/** /**
...@@ -234,7 +241,7 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -234,7 +241,7 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
data?.data, data?.data,
viewBinding.ivIdCardPortraitFace viewBinding.ivIdCardPortraitFace
) )
viewBinding.ivIdCardPortraitFace.setImageBitmap(bitmap) viewBinding.ivIdCardPortraitFace.addImage(bitmap)
FileUtil.generateFileFromUri( FileUtil.generateFileFromUri(
activity, activity,
data?.data, data?.data,
...@@ -250,14 +257,14 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -250,14 +257,14 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
data?.data, data?.data,
viewBinding.ivIdCardNationalEmblemFace viewBinding.ivIdCardNationalEmblemFace
) )
viewBinding.ivIdCardNationalEmblemFace.setImageBitmap(bitmap) viewBinding.ivIdCardNationalEmblemFace.addImage(bitmap)
FileUtil.generateFileFromUri( FileUtil.generateFileFromUri(
activity, activity,
data?.data, data?.data,
cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE
) )
BitmapUtil.compressImage( BitmapUtil.compressImage(
cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE, cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE,
10 10
) )
} }
...@@ -278,7 +285,7 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -278,7 +285,7 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
it it
) )
} }
viewBinding.ivIdCardPortraitFace.setImageBitmap(bitmap) viewBinding.ivIdCardPortraitFace.addImage(bitmap)
BitmapUtil.generateFileFromBitmap( BitmapUtil.generateFileFromBitmap(
bitmap, bitmap,
100, 100,
...@@ -291,7 +298,7 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -291,7 +298,7 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
it it
) )
} }
viewBinding.ivIdCardNationalEmblemFace.setImageBitmap(bitmap) viewBinding.ivIdCardNationalEmblemFace.addImage(bitmap)
BitmapUtil.generateFileFromBitmap( BitmapUtil.generateFileFromBitmap(
bitmap, bitmap,
100, 100,
...@@ -402,6 +409,7 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -402,6 +409,7 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
override fun getIDCardOCRSuccess(result: GetIDCardOCRBean.Response?) { override fun getIDCardOCRSuccess(result: GetIDCardOCRBean.Response?) {
// 存储认证使用的信息 // 存储认证使用的信息
LifeAccountAuthDataManager.personalAuthData.isIDCardAuthPass = true
result?.let { LifeAccountAuthDataManager.generatePersonalAuthData(it) } result?.let { LifeAccountAuthDataManager.generatePersonalAuthData(it) }
// 回显OCR结果 // 回显OCR结果
viewBinding.evRealName.fillEditContent(result?.posit?.name) viewBinding.evRealName.fillEditContent(result?.posit?.name)
...@@ -409,6 +417,8 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -409,6 +417,8 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
// 此时身份证两面均已上传并OCR识别完成 删除本地临时存储文件 // 此时身份证两面均已上传并OCR识别完成 删除本地临时存储文件
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE).delete() File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE).delete()
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE).delete() File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE).delete()
// 检查下一步条件
(activity as LifeAccountPersonalAuthActivity).checkNextCondition()
} }
override fun getIDCardOCRFailure(message: String?) { override fun getIDCardOCRFailure(message: String?) {
...@@ -416,6 +426,7 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -416,6 +426,7 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
Constant.LIFE_ACCOUNT_AUTH_TAG, Constant.LIFE_ACCOUNT_AUTH_TAG,
"request id card ocr failure, message: $message" "request id card ocr failure, message: $message"
) )
LifeAccountAuthDataManager.personalAuthData.isIDCardAuthPass = false
} }
override fun getTokenSuccess(t: HttpResult<AuthPersonalGetTokenBean.Response?>?) { override fun getTokenSuccess(t: HttpResult<AuthPersonalGetTokenBean.Response?>?) {
...@@ -435,17 +446,22 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -435,17 +446,22 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
override fun identifyIdOcrVerifySuccess(t: HttpResult<IdentifyIdOcrVerifyBean.Response?>?) { override fun identifyIdOcrVerifySuccess(t: HttpResult<IdentifyIdOcrVerifyBean.Response?>?) {
if (t?.result?.verify_result == true) { if (t?.result?.verify_result == true) {
Log.i(Constant.LIFE_ACCOUNT_AUTH_TAG, "live detect success") Log.i(Constant.LIFE_ACCOUNT_AUTH_TAG, "live detect success")
// 保存人脸验证结果
LifeAccountAuthDataManager.personalAuthData.isFaceAuthPass = true
// 人脸验证通过UI // 人脸验证通过UI
changeFaceAuthUI(true) changeFaceAuthUI(true)
// 删除活体检测数据 // 删除活体检测数据
File(cachePath + Constant.FILE_PATH_ALIVE_DETECT_VERIFY_DATA).delete() File(cachePath + Constant.FILE_PATH_ALIVE_DETECT_VERIFY_DATA).delete()
// 回调server 保存活体识别认证状态 // 回调server 保存活体识别认证状态
ApiService.authLiveIdentity(this) ApiService.authLiveIdentity(this)
// 检查下一步条件
(activity as LifeAccountPersonalAuthActivity).checkNextCondition()
} }
} }
override fun identifyIdOcrVerifyFailure(t: HttpResult<IdentifyIdOcrVerifyBean.Response?>?) { override fun identifyIdOcrVerifyFailure(t: HttpResult<IdentifyIdOcrVerifyBean.Response?>?) {
Log.e(Constant.LIFE_ACCOUNT_AUTH_TAG, "live detect failure, message: ${t?.reason}") Log.e(Constant.LIFE_ACCOUNT_AUTH_TAG, "live detect failure, message: ${t?.reason}")
LifeAccountAuthDataManager.personalAuthData.isFaceAuthPass = false
// 人脸验证未通过UI // 人脸验证未通过UI
changeFaceAuthUI(false) changeFaceAuthUI(false)
} }
...@@ -560,6 +576,18 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth ...@@ -560,6 +576,18 @@ class LifeAccountIDCardAuthFragment : BaseFragment<FragmentLifeAccountIdCardAuth
) )
} }
override fun onImageClear() {
viewBinding.evRealName.clearEditContent()
viewBinding.evIdCardNumber.clearEditContent()
// 检查下一步状态
(activity as LifeAccountPersonalAuthActivity).checkNextCondition()
}
override fun onTextClear() {
// 检查下一步状态
(activity as LifeAccountPersonalAuthActivity).checkNextCondition()
}
/** /**
* 人脸验证结果UI * 人脸验证结果UI
* @param result true为验证通过 false为验证未通过 * @param result true为验证通过 false为验证未通过
......
...@@ -2,6 +2,7 @@ package com.yidian.shenghuoquan.newscontent.ui.auth ...@@ -2,6 +2,7 @@ package com.yidian.shenghuoquan.newscontent.ui.auth
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.CompoundButton
import com.yidian.common.XRouterPathConstants import com.yidian.common.XRouterPathConstants
import com.yidian.common.base.BaseActivity import com.yidian.common.base.BaseActivity
import com.yidian.shenghuoquan.newscontent.R import com.yidian.shenghuoquan.newscontent.R
...@@ -14,7 +15,7 @@ import com.yidian.xpage.XPageManager ...@@ -14,7 +15,7 @@ import com.yidian.xpage.XPageManager
* description: 生活号个人认证 * description: 生活号个人认证
*/ */
class LifeAccountPersonalAuthActivity : BaseActivity<ActivityLifeAccountPersonalAuthBinding>(), class LifeAccountPersonalAuthActivity : BaseActivity<ActivityLifeAccountPersonalAuthBinding>(),
View.OnClickListener { View.OnClickListener, CompoundButton.OnCheckedChangeListener {
override fun createViewBinding(): ActivityLifeAccountPersonalAuthBinding { override fun createViewBinding(): ActivityLifeAccountPersonalAuthBinding {
return ActivityLifeAccountPersonalAuthBinding.inflate(layoutInflater) return ActivityLifeAccountPersonalAuthBinding.inflate(layoutInflater)
...@@ -39,9 +40,27 @@ class LifeAccountPersonalAuthActivity : BaseActivity<ActivityLifeAccountPersonal ...@@ -39,9 +40,27 @@ class LifeAccountPersonalAuthActivity : BaseActivity<ActivityLifeAccountPersonal
private fun initListener() { private fun initListener() {
viewBind.viewCommonHeader.ivBack.setOnClickListener(this) viewBind.viewCommonHeader.ivBack.setOnClickListener(this)
viewBind.svOccupation.setOnClickListener(this) viewBind.svOccupation.setOnClickListener(this)
viewBind.cbProtocol.setOnCheckedChangeListener(this)
viewBind.btnNext.setOnClickListener(this) viewBind.btnNext.setOnClickListener(this)
} }
/**
* 检查是否可以进行下一步
*/
fun checkNextCondition() {
if (viewBind.cbProtocol.isChecked
&& LifeAccountAuthDataManager.personalAuthData.occupation.isNotEmpty()
&& LifeAccountAuthDataManager.personalAuthData.isIDCardAuthPass
&& LifeAccountAuthDataManager.personalAuthData.isFaceAuthPass
) {
viewBind.btnNext.alpha = 1f
viewBind.btnNext.isEnabled = true
} else {
viewBind.btnNext.alpha = 0.32f
viewBind.btnNext.isEnabled = false
}
}
override fun onClick(v: View?) { override fun onClick(v: View?) {
when (v?.id) { when (v?.id) {
R.id.iv_back -> XPageManager.pop(null) R.id.iv_back -> XPageManager.pop(null)
...@@ -53,4 +72,9 @@ class LifeAccountPersonalAuthActivity : BaseActivity<ActivityLifeAccountPersonal ...@@ -53,4 +72,9 @@ class LifeAccountPersonalAuthActivity : BaseActivity<ActivityLifeAccountPersonal
} }
} }
} }
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
if (buttonView?.isPressed == false) return
checkNextCondition()
}
} }
\ No newline at end of file
...@@ -30,6 +30,8 @@ class LifeAccountAuthIdentityInfoEditView @JvmOverloads constructor( ...@@ -30,6 +30,8 @@ class LifeAccountAuthIdentityInfoEditView @JvmOverloads constructor(
) )
) )
private lateinit var callback: OnLifeAccountAuthIdentityInfoEditViewCallback
init { init {
val typedArray = val typedArray =
context.obtainStyledAttributes(attrs, R.styleable.LifeAccountAuthIdentityInfoEditView) context.obtainStyledAttributes(attrs, R.styleable.LifeAccountAuthIdentityInfoEditView)
...@@ -89,4 +91,15 @@ class LifeAccountAuthIdentityInfoEditView @JvmOverloads constructor( ...@@ -89,4 +91,15 @@ class LifeAccountAuthIdentityInfoEditView @JvmOverloads constructor(
// 处理一键清除按钮 // 处理一键清除按钮
viewBinding.ivClear.visibility = if (TextUtils.isEmpty(s)) View.GONE else View.VISIBLE viewBinding.ivClear.visibility = if (TextUtils.isEmpty(s)) View.GONE else View.VISIBLE
} }
fun setOnLifeAccountAuthIdentityInfoEditViewCallback(callback: OnLifeAccountAuthIdentityInfoEditViewCallback) {
this.callback = callback
}
interface OnLifeAccountAuthIdentityInfoEditViewCallback {
/**
* 清除文字
*/
fun onTextClear()
}
} }
\ No newline at end of file
package com.yidian.shenghuoquan.newscontent.widget
import android.content.Context
import android.graphics.Bitmap
import android.util.AttributeSet
import android.view.View
import androidx.core.view.isVisible
import com.yidian.nightmode.widget.YdConstraintLayout
import com.yidian.shenghuoquan.newscontent.R
import com.yidian.shenghuoquan.newscontent.databinding.ViewLifeAccountAuthImageBinding
/**
* author: yinjiacheng
* date: 6/2/21 6:09 PM
* description: 生活号认证 身份证上传
*/
class LifeAccountAuthImageView @JvmOverloads constructor(
context: Context,
val attrs: AttributeSet? = null,
defStyle: Int = 0
) : YdConstraintLayout(context, attrs, defStyle), View.OnClickListener {
private val viewBinding = ViewLifeAccountAuthImageBinding.bind(
View.inflate(
getContext(),
R.layout.view_life_account_auth_image,
this
)
)
private lateinit var callback: OnLifeAccountAuthImageViewCallback
init {
val typeArray =
getContext().obtainStyledAttributes(attrs, R.styleable.LifeAccountAuthImageView)
viewBinding.ivPlaceHolder.setImageResource(
typeArray.getResourceId(
R.styleable.LifeAccountAuthImageView_LifeAccountAuthImageView_place_holder_image,
-1
)
)
typeArray.recycle()
viewBinding.ivClear.setOnClickListener(this)
}
/**
* 填充图片
* @param resId 图片资源id
*/
fun addImage(resId: Int) {
viewBinding.ivContent.setImageResource(resId)
isEnabled = false
viewBinding.ivClear.isVisible = true
}
/**
* 填充图片
* @param bitmap 图片bitmap
*/
fun addImage(bitmap: Bitmap?) {
viewBinding.ivContent.setImageBitmap(bitmap)
isEnabled = false
viewBinding.ivClear.isVisible = true
}
/**
* 清除图片
*/
fun clearImage() {
viewBinding.ivContent.setImageDrawable(null)
isEnabled = true
viewBinding.ivClear.isVisible = false
}
override fun onClick(v: View?) {
if (v?.id == R.id.iv_clear) {
// 清除填充的图片
clearImage()
}
}
fun setOnLifeAccountAuthImageViewCallback(callback: OnLifeAccountAuthImageViewCallback) {
this.callback = callback
}
interface OnLifeAccountAuthImageViewCallback {
/**
* 清除图片
*/
fun onImageClear()
}
}
\ No newline at end of file
...@@ -79,6 +79,7 @@ ...@@ -79,6 +79,7 @@
android:layout_width="302dp" android:layout_width="302dp"
android:layout_height="42dp" android:layout_height="42dp"
android:layout_marginBottom="33dp" android:layout_marginBottom="33dp"
android:alpha="0.32"
android:background="@drawable/bg_btn_auth" android:background="@drawable/bg_btn_auth"
android:text="@string/auth_finish" android:text="@string/auth_finish"
android:textColor="#FFFFFFFF" android:textColor="#FFFFFFFF"
......
...@@ -17,22 +17,22 @@ ...@@ -17,22 +17,22 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<com.yidian.nightmode.widget.YdImageView <com.yidian.shenghuoquan.newscontent.widget.LifeAccountAuthImageView
android:id="@+id/iv_id_card_portrait_face" android:id="@+id/iv_id_card_portrait_face"
android:layout_width="156dp" android:layout_width="156dp"
android:layout_height="97dp" android:layout_height="97dp"
android:layout_marginStart="19dp" android:layout_marginStart="19dp"
android:layout_marginTop="17dp" android:layout_marginTop="17dp"
android:src="@mipmap/img_id_card_portrait" app:LifeAccountAuthImageView_place_holder_image="@mipmap/img_id_card_portrait"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_id_card_type" /> app:layout_constraintTop_toBottomOf="@id/tv_id_card_type" />
<com.yidian.nightmode.widget.YdImageView <com.yidian.shenghuoquan.newscontent.widget.LifeAccountAuthImageView
android:id="@+id/iv_id_card_national_emblem_face" android:id="@+id/iv_id_card_national_emblem_face"
android:layout_width="156dp" android:layout_width="156dp"
android:layout_height="97dp" android:layout_height="97dp"
android:layout_marginEnd="19dp" android:layout_marginEnd="19dp"
android:src="@mipmap/img_id_card_national_emblem" app:LifeAccountAuthImageView_place_holder_image="@mipmap/img_id_card_national_emblem"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/iv_id_card_portrait_face" /> app:layout_constraintTop_toTopOf="@id/iv_id_card_portrait_face" />
......
<?xml version="1.0" encoding="utf-8"?>
<com.yidian.nightmode.widget.YdConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.yidian.nightmode.widget.YdImageView
android:id="@+id/iv_place_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.yidian.nightmode.widget.YdImageView
android:id="@+id/iv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.yidian.nightmode.widget.YdImageView
android:id="@+id/iv_clear"
android:layout_width="23dp"
android:layout_height="23dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="2dp"
android:src="@mipmap/icon_clear"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@id/iv_content"
app:layout_constraintTop_toTopOf="@id/iv_content" />
</com.yidian.nightmode.widget.YdConstraintLayout>
\ No newline at end of file
...@@ -16,4 +16,8 @@ ...@@ -16,4 +16,8 @@
<attr name="LifeAccountAuthBusinessInfoEditView_hint_text" format="string" /> <attr name="LifeAccountAuthBusinessInfoEditView_hint_text" format="string" />
</declare-styleable> </declare-styleable>
<declare-styleable name="LifeAccountAuthImageView">
<attr name="LifeAccountAuthImageView_place_holder_image" format="reference" />
</declare-styleable>
</resources> </resources>
\ No newline at end of file
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