Commit 33ff1a8f authored by shiyl's avatar shiyl

Merge branch 'dev_commodity' of...

Merge branch 'dev_commodity' of https://git.yidian-inc.com:8021/bp/ShenghuoquanBusiness into dev_commodity
parents 5fa0259e 44f6a5e8
......@@ -93,7 +93,6 @@ dependencies {
implementation('com.yidian.android:CommonComponent:0.0.88') {
exclude(group: 'com.yidian.android', module: 'NetworkImageLib')
}
implementation files('libs/ks3-androidsdk-1.4.9.jar')
api 'com.github.bumptech.glide:glide:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0'
......
......@@ -16,7 +16,9 @@ import com.yidian.common.bean.LocalSelectedImageBean
import com.yidian.common.bean.SelectedImageBean
import com.yidian.common.bean.UploadImageBean
import com.yidian.common.utils.GlideEngine
import com.yidian.common.utils.KS3Core
import com.yidian.framework.mobile.insight.manage.XInsight
import com.yidian.framework.mobile.insight.storage.ImageUploadListener
import com.yidian.framework.mobile.insight.storage.bean.ImageUploadBean
import com.yidian.xarc.xrouter.ResultResolver
import com.yidian.xpage.observer.DStackActivityManager
import com.yidian.yac.core.zap.ZapService
......@@ -35,7 +37,6 @@ import kotlin.collections.ArrayList
* 提供调用原生相机拍照、图片选择器、图片预览、图片上传能力
*/
// TODO: 7/9/21 动态获取权限
// TODO: 7/9/21 清除缓存
class ImageService : ZapService() {
companion object {
......@@ -250,35 +251,38 @@ class ImageService : ZapService() {
resolver.fail(CODE_FILE_NOT_EXIST, "file not exist", "文件不存在")
return
}
KS3Core.INSTANCE.uploadObject(file, KS3Core.ObjectType.IMAGE, KS3Core.ScenarioType.COMMODITY, object : KS3Core.OnKS3TaskListener {
override fun onTaskStart() {
XInsight.uploadImage(
file, ScenarioType.COMMODITY.scenario,
object : ImageUploadListener {
override fun onTaskStart(tag: String?) {
}
}
override fun onTaskProgress(progress: Double) {
override fun onTaskProgress(progress: Double?, tag: String?) {
}
}
override fun onTaskFinish() {
override fun onTaskFinish(tag: String?) {
}
}
override fun onTaskCancel() {
override fun onTaskCancel(tag: String?) {
}
}
override fun onTaskSuccess(bucket: String?, objectKey: String?) {
resolver.success(JSONObject(gson.toJson(UploadImageBean(id, "${bucket}/${objectKey}"))))
// 删除临时文件
clearCache(picOriginalPath + it.data.fileName, it.data.compressPath)
}
override fun onTaskSuccess(bucket: String?, objectKey: String?, imageUploadBean: ImageUploadBean?, tag: String?) {
resolver.success(JSONObject(gson.toJson(imageUploadBean?.image_id?.let { imageId -> UploadImageBean(id, imageId) })))
// 删除临时文件
clearCache(picOriginalPath + it.data.fileName, it.data.compressPath)
}
override fun onTaskFailure(statesCode: Int, message: String?) {
resolver.fail(CODE_FILE_UPLOAD_FAIL, "upload failed", "上传失败")
// 删除临时文件
clearCache(picOriginalPath + it.data.fileName, it.data.compressPath)
}
})
override fun onTaskFailure(code: Int, msg: String?, tag: String?) {
resolver.fail(CODE_FILE_UPLOAD_FAIL, "upload failed", "上传失败")
// 删除临时文件
clearCache(picOriginalPath + it.data.fileName, it.data.compressPath)
}
}, id
)
} ?: resolver.fail(CODE_FILE_NOT_EXIST, "file not exist", "文件不存在")
}
......@@ -306,4 +310,13 @@ class ImageService : ZapService() {
File(compressPath).apply { if (exists()) delete() }
}
/**
* 业务场景类型
* 图片上传时需要区分
*/
enum class ScenarioType(val scenario: String) {
// 商品
COMMODITY("shq_goods")
}
}
package com.yidian.common.utils
import android.content.Context
import android.graphics.Bitmap
import id.zelory.compressor.Compressor
import id.zelory.compressor.constraint.destination
import id.zelory.compressor.constraint.format
import id.zelory.compressor.constraint.quality
import id.zelory.compressor.constraint.resolution
import kotlinx.coroutines.Dispatchers
import java.io.File
/**
* author: yinjiacheng
* date: 7/15/21 10:55 PM
* description: 图片压缩工具
* https://github.com/zetbaitsu/Compressor
*/
object ImageCompressUtils {
/**
* 图片压缩
* @param srcFile 源文件
* @param destFile 目标文件
* @param destWidth 目标宽度
* @param destHeight 目标高度
* @param quality 压缩质量
* @param format 压缩格式
* @param syncOrAsync 同步or异步
*/
suspend fun compress(
context: Context,
srcFile: File,
destFile: File? = null,
destWidth: Int? = null,
destHeight: Int? = null,
quality: Int? = null,
format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
syncOrAsync: Boolean = false
): File {
return Compressor.compress(context, srcFile, if (syncOrAsync) Dispatchers.Main else Dispatchers.IO) {
destFile?.let { destination(it) }
destWidth?.let { width -> destHeight?.let { height -> resolution(width, height) } }
quality?.let { quality(it) }
format(format)
}
}
}
......@@ -74,9 +74,6 @@ dependencies {
implementation rootProject.ext.dependencies.publicImplementation
testImplementation rootProject.ext.dependencies.testImplementation
androidTestImplementation rootProject.ext.dependencies.androidTestImplementation
implementation(name: 'meglive_still', ext: 'aar')
implementation project(':idcard_quality')
implementation files('libs/ks3-androidsdk-1.4.9.jar')
implementation ('com.yidian.android:CommonComponent:0.0.88'){
exclude(group: 'com.yidian.android', module: 'NetworkImageLib')
}
......
......@@ -112,4 +112,9 @@ object Constant {
// 服务端返回的业务错误码
const val ERROR_CODE_PERSONAL_AUTH_INFO_DUPLICATE = 2103010 // 身份证号已认证并且认证手机号不是当前手机号
// 身份证、营业执照压缩参数
const val COMPRESS_DEST_WIDTH = 480 // 目标宽度
const val COMPRESS_DEST_HEIGHT = 640 // 目标高度
const val COMPRESS_DEST_QUALITY = 50 // 目标质量
}
......@@ -9,7 +9,6 @@ import com.yidian.common.HawkConfig
import com.yidian.common.XRouterPathConstants
import com.yidian.common.XRouterPathConstants.Companion.LOGIN_LIFE_CIRCLE
import com.yidian.common.base.BaseActivity
import com.yidian.common.extensions.clickAntiShake
import com.yidian.common.extensions.hide
import com.yidian.common.extensions.show
import com.yidian.common.http.HttpResult
......@@ -22,7 +21,7 @@ import com.yidian.shenghuoquan.newscontent.http.callback.IGetLifeAccountListCall
import com.yidian.shenghuoquan.newscontent.http.httpbean.IMobileLoginCallback
import com.yidian.shenghuoquan.newscontent.http.httpbean.LifeAccountItemBean
import com.yidian.shenghuoquan.newscontent.http.httpbean.MobileLoginBean
import com.yidian.shenghuoquan.newscontent.ui.auth.LifeAccountAuthDataManager
import com.yidian.shenghuoquan.newscontent.ui.auth.LifeAccountAuthDataManagerV2
import com.yidian.shenghuoquan.newscontent.ui.auth.LifeAccountIdentityActivity
import com.yidian.shenghuoquan.newscontent.ui.center.MerchantCenterActivity
import com.yidian.shenghuoquan.newscontent.utils.CountDownTimerUtils
......@@ -170,8 +169,8 @@ class LoginLifeCircleActivity : BaseActivity<ActivityLoginBinding>(), IGetLifeAc
// 生活号id
StorageUtil.deleteLifeAccountId()
// 清除内存中的认证数据
LifeAccountAuthDataManager.personalAuthData.clearData()
LifeAccountAuthDataManager.merchantAuthData.clearData()
LifeAccountAuthDataManagerV2.personalAuthData.clearData()
LifeAccountAuthDataManagerV2.merchantAuthData.clearData()
}
override fun onDestroy() {
......
package com.yidian.shenghuoquan.newscontent.ui.auth
import com.yidian.framework.mobile.insight.identity.IdentityCardResult
import com.yidian.framework.mobile.insight.ocr.BusinessLicenseResult
import com.yidian.shenghuoquan.newscontent.bean.LifeAccountMerchantAuthData
import com.yidian.shenghuoquan.newscontent.bean.LifeAccountPersonalAuthData
import com.yidian.shenghuoquan.newscontent.constant.Constant
import com.yidian.shenghuoquan.newscontent.http.httpbean.AuthAuthenticationBean
import com.yidian.shenghuoquan.newscontent.http.httpbean.AuthMerchantCheckBean
/**
* author: yinjiacheng
* date: 7/15/21 5:32 PM
* description: 生活号认证数据管理 接入XInsight
*/
object LifeAccountAuthDataManagerV2 {
val personalAuthData by lazy { LifeAccountPersonalAuthData() }
val merchantAuthData by lazy { LifeAccountMerchantAuthData() }
/**
* 将身份证人像面OCR结果填入
*/
fun generateIDCardPortraitFaceData(data: IdentityCardResult) {
personalAuthData.idCardPortraitFaceCompleteness = data.posit.completeness
personalAuthData.idCardNum = data.posit.idcard_number
personalAuthData.gender = data.posit.gender
personalAuthData.realName = data.posit.name
personalAuthData.address = data.posit.address
personalAuthData.nationality = data.posit.nationality
}
/**
* 将身份证国徽面OCR结果填入
*/
fun generateIDCardNationEmblemFaceData(data: IdentityCardResult) {
personalAuthData.idCardNationalEmblemFaceCompleteness = data.back.completeness
personalAuthData.validDateStart = data.back.valid_date_start
personalAuthData.validDateEnd = data.back.valid_date_end
personalAuthData.issuedBy = data.back.issued_by
}
/**
* 填入个人认证流程回显数据
* 身份证两面完整度此时一定是0
*/
fun generatePersonalAuthData(data: AuthAuthenticationBean.Response) {
personalAuthData.isFaceAuthPass = data.live_identity_status == Constant.ALIVE_DETECT_SUCCESS
personalAuthData.realName = data.full_name
personalAuthData.idCardNum = data.id_card
personalAuthData.occupation = data.occupation
personalAuthData.idCardPortraitFaceUrl = data.posit_image_url
personalAuthData.idCardNationalEmblemFaceUrl = data.back_image_url
personalAuthData.address = data.address
personalAuthData.nationality = data.nationality
personalAuthData.gender = data.gender
personalAuthData.validDateStart = data.validity_start_time
personalAuthData.validDateEnd = data.validity_end_time
personalAuthData.issuedBy = data.issued_by
personalAuthData.isIDCardPortraitFaceUpload = true
personalAuthData.isIDCardNationalEmblemFaceUpload = true
personalAuthData.idCardPortraitFaceCompleteness = 0
personalAuthData.idCardNationalEmblemFaceCompleteness = 0
}
/**
* 生成身份信息校验接口请求体
*/
fun generateAuthPersonalCheckRequest(): HashMap<String, String?> {
val requestMap = HashMap<String, String?>()
requestMap["posit_image_url"] = personalAuthData.idCardPortraitFaceUrl
requestMap["back_image_url"] = personalAuthData.idCardNationalEmblemFaceUrl
requestMap["front_completeness"] = personalAuthData.idCardPortraitFaceCompleteness.toString()
requestMap["bak_completeness"] = personalAuthData.idCardNationalEmblemFaceCompleteness.toString()
requestMap["id_card"] = personalAuthData.idCardNum
requestMap["gender"] = personalAuthData.gender
requestMap["name"] = personalAuthData.realName
requestMap["occupation"] = personalAuthData.occupation
requestMap["address"] = personalAuthData.address
requestMap["nationality"] = personalAuthData.nationality
requestMap["valid_date_start"] = personalAuthData.validDateStart
requestMap["valid_date_end"] = personalAuthData.validDateEnd
requestMap["issued_by"] = personalAuthData.issuedBy
return requestMap
}
/**
* 将营业执照OCR结果填入
*/
fun generateBusinessLicenseData(data: BusinessLicenseResult) {
merchantAuthData.registrationName = data.name
merchantAuthData.registrationCode = data.code
merchantAuthData.type = data.type
merchantAuthData.addr = data.addr
merchantAuthData.pers = data.pers
merchantAuthData.scope = data.scope
merchantAuthData.time = data.time
merchantAuthData.regi = data.regi
merchantAuthData.organizer = data.organizer
merchantAuthData.date = data.date
merchantAuthData.comp = data.comp
merchantAuthData.num = data.num
merchantAuthData.form = data.form
merchantAuthData.funding = data.funding
merchantAuthData.dateIssue = data.date_issue
}
/**
* 填入企业认证回显数据
* 身份证两面完整度此时一定是0
*/
fun generateMerchantAuthData(data: AuthMerchantCheckBean.Response) {
merchantAuthData.authRecordId = data.enterprise_auth_record_id
merchantAuthData.registrationName = data.name
merchantAuthData.registrationCode = data.code
merchantAuthData.merchantType = data.data_type
merchantAuthData.isBusinessLicenseUpload = true
merchantAuthData.type = data.enterprice_type
merchantAuthData.addr = data.enterprice_address
merchantAuthData.scope = data.businesss_scope
merchantAuthData.time = data.term_time
merchantAuthData.regi = data.registered_capital
merchantAuthData.organizer = data.organizer
merchantAuthData.date = data.establishment_date
merchantAuthData.comp = data.registration_authority
merchantAuthData.num = data.num
merchantAuthData.form = data.form
merchantAuthData.funding = data.funding
merchantAuthData.dateIssue = data.issue_date
merchantAuthData.businessLicenseUrl = data.business_image_url
merchantAuthData.pers = data.pers
personalAuthData.realName = data.legal_person
personalAuthData.idCardNum = data.legal_id_card
personalAuthData.phoneNum = data.mobile
personalAuthData.isFaceAuthPass = data.live_identity_status == Constant.ALIVE_DETECT_SUCCESS
personalAuthData.address = data.legal_address
personalAuthData.nationality = data.legal_nationality
personalAuthData.issuedBy = data.legal_issued_by
personalAuthData.validDateStart = data.legal_validity_start_time
personalAuthData.validDateEnd = data.legal_validity_end_time
personalAuthData.idCardPortraitFaceUrl = data.posit_image_url
personalAuthData.idCardNationalEmblemFaceUrl = data.back_image_url
personalAuthData.isIDCardPortraitFaceUpload = true
personalAuthData.isIDCardNationalEmblemFaceUpload = true
personalAuthData.idCardPortraitFaceCompleteness = 0
personalAuthData.idCardNationalEmblemFaceCompleteness = 0
}
/**
* 生成营业执照上传接口请求体
*/
fun generateAuthBusinessLicenseCommitRequest(
lifeAccountId: String?, recordId: Long?
): HashMap<String, String?> {
val requestMap = HashMap<String, String?>()
requestMap["code"] = merchantAuthData.registrationCode
requestMap["name"] = merchantAuthData.registrationName
requestMap["type"] = merchantAuthData.type
requestMap["addr"] = merchantAuthData.addr
requestMap["pers"] = merchantAuthData.pers
requestMap["scope"] = merchantAuthData.scope
requestMap["time"] = merchantAuthData.time
requestMap["regi"] = merchantAuthData.regi
requestMap["organizer"] = merchantAuthData.organizer
requestMap["date"] = merchantAuthData.date
requestMap["comp"] = merchantAuthData.comp
requestMap["num"] = merchantAuthData.num
requestMap["form"] = merchantAuthData.form
requestMap["funding"] = merchantAuthData.funding
requestMap["issue_date"] = merchantAuthData.dateIssue
requestMap["entterprise_image_url"] = merchantAuthData.businessLicenseUrl
requestMap["data_from"] = Constant.DATA_FROM_B.toString()
requestMap["data_type"] = merchantAuthData.merchantType.toString()
lifeAccountId?.let { requestMap["life_account_id"] = it }
recordId?.let { requestMap["enterprise_auth_record_id"] = it.toString() }
return requestMap
}
/**
* 生成个体工商户身份上传接口请求体
*/
fun generateAuthIndividualBusinessIdentityCommitRequest(): HashMap<String, String?> {
val requestMap = HashMap<String, String?>()
requestMap["record_id"] = merchantAuthData.authRecordId.toString()
requestMap["posit_image_url"] = personalAuthData.idCardPortraitFaceUrl
requestMap["back_image_url"] = personalAuthData.idCardNationalEmblemFaceUrl
requestMap["front_completeness"] = personalAuthData.idCardPortraitFaceCompleteness.toString()
requestMap["bak_completeness"] = personalAuthData.idCardNationalEmblemFaceCompleteness.toString()
requestMap["idcard_number"] = personalAuthData.idCardNum
requestMap["gender"] = personalAuthData.gender
requestMap["name"] = personalAuthData.realName
requestMap["address"] = personalAuthData.address
requestMap["nationality"] = personalAuthData.nationality
requestMap["valid_date_start"] = personalAuthData.validDateStart
requestMap["valid_date_end"] = personalAuthData.validDateEnd
requestMap["issued_by"] = personalAuthData.issuedBy
return requestMap
}
/**
* 生成普通企业法人身份信息上传接口请求体
*/
fun generateAuthEnterpriseLegalIdentityCommitRequest(
lifeAccountId: String?
): HashMap<String, String?> {
val requestMap = HashMap<String, String?>()
requestMap["record_id"] = merchantAuthData.authRecordId.toString()
requestMap["posit_image_url"] = personalAuthData.idCardPortraitFaceUrl
requestMap["back_image_url"] = personalAuthData.idCardNationalEmblemFaceUrl
requestMap["front_completeness"] = personalAuthData.idCardPortraitFaceCompleteness.toString()
requestMap["bak_completeness"] = personalAuthData.idCardNationalEmblemFaceCompleteness.toString()
requestMap["idcard_number"] = personalAuthData.idCardNum
requestMap["gender"] = personalAuthData.gender
requestMap["name"] = personalAuthData.realName
requestMap["address"] = personalAuthData.address
requestMap["nationality"] = personalAuthData.nationality
requestMap["valid_date_start"] = personalAuthData.validDateStart
requestMap["valid_date_end"] = personalAuthData.validDateEnd
requestMap["issued_by"] = personalAuthData.issuedBy
requestMap["mobile"] = personalAuthData.phoneNum
lifeAccountId?.let { requestMap["life_account_id"] = it }
return requestMap
}
/**
* 生成个体工商户认证完成接口请求体
*/
fun generateAuthIndividualBusinessCompleteRequest(lifeAccountId: String?): HashMap<String, String?> {
val requestMap = HashMap<String, String?>()
requestMap["record_id"] = merchantAuthData.authRecordId.toString()
lifeAccountId?.let { requestMap["life_account_id"] = it }
return requestMap
}
}
package com.yidian.shenghuoquan.newscontent.ui.auth
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.text.InputFilter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide
import com.tbruyelle.rxpermissions3.RxPermissions
import com.yidian.common.base.BaseFragment
import com.yidian.common.constant.GlobalConstants
import com.yidian.common.utils.ImageCompressUtils
import com.yidian.framework.mobile.insight.manage.XInsight
import com.yidian.framework.mobile.insight.ocr.BusinessLicenseCallBack
import com.yidian.framework.mobile.insight.ocr.BusinessLicenseResult
import com.yidian.shenghuoquan.newscontent.R
import com.yidian.shenghuoquan.newscontent.adapter.BottomSelectAdapter
import com.yidian.shenghuoquan.newscontent.bean.BottomSelectBean
import com.yidian.shenghuoquan.newscontent.constant.Constant
import com.yidian.shenghuoquan.newscontent.databinding.FragmentLifeAccountBusinessLicenseAuthBinding
import com.yidian.shenghuoquan.newscontent.http.httpbean.AuthMerchantCheckBean
import com.yidian.shenghuoquan.newscontent.ui.dialog.BottomSelectDialog
import com.yidian.shenghuoquan.newscontent.utils.BitmapUtil
import com.yidian.shenghuoquan.newscontent.utils.FileUtil
import com.yidian.shenghuoquan.newscontent.utils.InputFilterUtil
import com.yidian.shenghuoquan.newscontent.widget.LifeAccountAuthBusinessInfoEditView
import com.yidian.utils.ToastUtil
import kotlinx.coroutines.launch
import java.io.File
/**
* author: yinjiacheng
* date: 7/15/21 7:38 PM
* description: 生活号认证 营业执照认证 接入XInsight
*/
class LifeAccountBusinessLicenseAuthFragmentV2 :
BaseFragment<FragmentLifeAccountBusinessLicenseAuthBinding>(), View.OnClickListener,
BottomSelectAdapter.OnItemClickListener<BottomSelectBean>,
LifeAccountAuthBusinessInfoEditView.OnLifeAccountAuthBusinessInfoEditViewCallback, BusinessLicenseCallBack {
companion object {
// 认证数据
const val EXTRA_AUTH_DATA = "authData"
}
/**
* sdcard/Android/data/package/cache
*/
private val commonPath by lazy { activity?.externalCacheDir?.absolutePath }
/**
* 调用系统相机拍照,照片保存位置的Uri
*/
private var photoUri: Uri? = null
override fun createViewBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentLifeAccountBusinessLicenseAuthBinding {
return FragmentLifeAccountBusinessLicenseAuthBinding.inflate(inflater, container, false)
}
override fun init(savedInstanceState: Bundle?) {
super.init(savedInstanceState)
initView()
initListener()
initData()
}
private fun initView() {
// 输入过滤
// 企业名称限制40汉字
viewBinding.evRegistrationName.setInputFilters(InputFilterUtil.getRegistrationNameFilter(), InputFilter.LengthFilter(40))
// 统一信用代码限制18位数字+大小写英文字母 小写字母自动转换大写字母
viewBinding.evRegistrationCode.setInputFilters(
InputFilterUtil.getRegistrationCodeFilter(),
InputFilter.LengthFilter(18),
InputFilter.AllCaps()
)
}
private fun initListener() {
viewBinding.clBusinessLicenseUpload.setOnClickListener(this)
viewBinding.ivClear.setOnClickListener(this)
viewBinding.evRegistrationName.setOnLifeAccountAuthBusinessInfoEditViewCallback(this)
viewBinding.evRegistrationCode.setOnLifeAccountAuthBusinessInfoEditViewCallback(this)
}
private fun initData() {
// 还原认证数据
val authData =
arguments?.getSerializable(EXTRA_AUTH_DATA) ?: return
fillAuthInfo(authData as AuthMerchantCheckBean.Response)
}
/**
* 认证流程中断,下次进入认证流程时还原进度
*/
private fun fillAuthInfo(data: AuthMerchantCheckBean.Response) {
// 营业执照区域
Glide.with(this).load(data.entterprise_image_url).into(viewBinding.ivBusinessLicense)
viewBinding.ivClear.isVisible = true
viewBinding.clBusinessLicenseUpload.isEnabled = false
// 填入注册名称和注册码
viewBinding.evRegistrationName.fillEditContent(data.name)
viewBinding.evRegistrationCode.fillEditContent(data.code)
// 检查下一步条件
(activity as LifeAccountEnterpriseAuthActivity).checkNextCondition()
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.cl_business_license_upload -> {
// 弹出上传方式选择
context?.let {
BottomSelectDialog(
it,
this,
arrayListOf(
BottomSelectBean(Constant.ITEM_TAKE_PHOTO),
BottomSelectBean(Constant.ITEM_OPEN_ALBUM)
)
).show()
}
}
R.id.iv_clear -> {
clearBusinessLicenseInfo()
// 检查下一步条件
(activity as LifeAccountEnterpriseAuthActivity).checkNextCondition()
}
}
}
override fun onItemClick(view: View, position: Int, data: BottomSelectBean) {
when (data.item) {
Constant.ITEM_TAKE_PHOTO -> {
// 调用系统相机进行营业执照采集
val file = File(commonPath + Constant.FILE_PATH_BUSINESS_LICENSE_CAMERA)
if (file.exists()) file.delete()
if (file.parentFile?.exists() == false) file.parentFile?.mkdirs()
file.createNewFile()
photoUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//适配Android 7.0文件权限,通过FileProvider创建一个content类型的Uri
activity?.let {
FileProvider.getUriForFile(
it,
GlobalConstants.AUTHORITY,
file
)
}
} else {
Uri.fromFile(file)
}
// 判断camera权限
RxPermissions(this).request(Manifest.permission.CAMERA).subscribe {
if (it) {
startActivityForResult(
Intent(MediaStore.ACTION_IMAGE_CAPTURE).putExtra(
MediaStore.EXTRA_OUTPUT,
photoUri
),
Constant.REQUEST_CODE_OPEN_CAMERA
)
} else {
ToastUtil.showToast(activity, "请在设置里同意相关权限")
}
}
}
Constant.ITEM_OPEN_ALBUM -> {
// 打开系统相册
startActivityForResult(
Intent(Intent.ACTION_PICK).setDataAndType(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
"image/*"
),
Constant.REQUEST_CODE_OPEN_ALBUM
)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
val uri = if (requestCode == Constant.REQUEST_CODE_OPEN_ALBUM) data?.data else photoUri
val bitmap =
BitmapUtil.generateBitmapFromUri(activity, uri, viewBinding.ivBusinessLicense)
viewBinding.ivBusinessLicense.setImageBitmap(bitmap)
FileUtil.generateFileFromUri(
activity, uri,
commonPath + Constant.FILE_PATH_BUSINESS_LICENSE
)
// 删除从相机拍摄的照片
if (requestCode == Constant.REQUEST_CODE_OPEN_CAMERA) File(commonPath + Constant.FILE_PATH_BUSINESS_LICENSE_CAMERA).delete()
// 显示清除按钮
viewBinding.ivClear.visibility = View.VISIBLE
// 上传区域不可点击
viewBinding.clBusinessLicenseUpload.isEnabled = false
// 执行营业执照OCR
startBusinessLicenseOCR()
}
}
/**
* 执行营业执照OCR
*/
private fun startBusinessLicenseOCR() {
// 上传前压缩图像
lifecycleScope.launch {
activity?.let {
ImageCompressUtils.compress(
it,
File(commonPath + Constant.FILE_PATH_BUSINESS_LICENSE),
File(commonPath + Constant.FILE_PATH_BUSINESS_LICENSE),
Constant.COMPRESS_DEST_WIDTH, Constant.COMPRESS_DEST_HEIGHT, Constant.COMPRESS_DEST_QUALITY
)
}
XInsight.getBusinessLicenseOcrInfo(
File(commonPath + Constant.FILE_PATH_BUSINESS_LICENSE),
this@LifeAccountBusinessLicenseAuthFragmentV2
)
}
}
/**
* 清除已输入的营业执照相关信息
* 用户更换商户类型操作
*/
fun clearBusinessLicenseInfo() {
// 清除已上传的营业执照
viewBinding.ivBusinessLicense.setImageDrawable(null)
// 清除错误提示
viewBinding.tvUploadErrorTips.isVisible = false
activity?.let { viewBinding.viewBusinessLicenseUploadDivider.setBackgroundColor(ContextCompat.getColor(it, R.color.color_FFF2F2F2)) }
viewBinding.ivClear.isVisible = false
// 清除OCR或用户输入内容
viewBinding.evRegistrationName.clearEditContent()
viewBinding.evRegistrationCode.clearEditContent()
// 设置上传区域可点击
viewBinding.clBusinessLicenseUpload.isEnabled = true
LifeAccountAuthDataManagerV2.merchantAuthData.isBusinessLicenseUpload = false
LifeAccountAuthDataManagerV2.merchantAuthData.registrationName = null
LifeAccountAuthDataManagerV2.merchantAuthData.registrationCode = null
// 标识修改
LifeAccountAuthDataManagerV2.merchantAuthData.isAuthInfoModify = true
}
override fun onTextClear(from: LifeAccountAuthBusinessInfoEditView) {
// 标识修改
LifeAccountAuthDataManagerV2.merchantAuthData.isAuthInfoModify = true
if (from == viewBinding.evRegistrationName) {
LifeAccountAuthDataManagerV2.merchantAuthData.registrationName = ""
} else {
LifeAccountAuthDataManagerV2.merchantAuthData.registrationCode = ""
}
// 检查下一步条件
(activity as LifeAccountEnterpriseAuthActivity).checkNextCondition()
}
override fun onTextChange(from: LifeAccountAuthBusinessInfoEditView, text: String) {
if (from == viewBinding.evRegistrationName) {
// 标识修改
if (text != LifeAccountAuthDataManagerV2.merchantAuthData.registrationName) {
LifeAccountAuthDataManagerV2.merchantAuthData.isAuthInfoModify = true
}
LifeAccountAuthDataManagerV2.merchantAuthData.registrationName = text
} else {
if (text != LifeAccountAuthDataManagerV2.merchantAuthData.registrationCode) {
LifeAccountAuthDataManagerV2.merchantAuthData.isAuthInfoModify = true
}
LifeAccountAuthDataManagerV2.merchantAuthData.registrationCode = text
}
// 检查下一步条件
(activity as LifeAccountEnterpriseAuthActivity).checkNextCondition()
}
override fun onSucceed(businessLicenseResult: BusinessLicenseResult?, result: String?) {
activity?.runOnUiThread {
businessLicenseResult?.let { LifeAccountAuthDataManagerV2.generateBusinessLicenseData(it) }
// 回显OCR结果
viewBinding.evRegistrationName.fillEditContent(businessLicenseResult?.name)
viewBinding.evRegistrationCode.fillEditContent(businessLicenseResult?.code)
// 此时营业执照已上传并OCR识别完成 删除本地临时存储文件
File(commonPath + Constant.FILE_PATH_BUSINESS_LICENSE).delete()
// 检查下一步条件
activity?.let { (it as LifeAccountEnterpriseAuthActivity).checkNextCondition() }
}
}
override fun onFail(code: Int, errMsg: String?, errDomain: String?) {
activity?.runOnUiThread {
// 营业执照OCR识别失败提示
viewBinding.tvUploadErrorTips.isVisible = true
viewBinding.tvUploadErrorTips.text =
resources.getString(R.string.check_business_license_distinct)
activity?.let { viewBinding.viewBusinessLicenseUploadDivider.setBackgroundColor(ContextCompat.getColor(it, R.color.color_FFFF3A3A)) }
}
}
override fun onUploadTask(imageUrl: String?) {
LifeAccountAuthDataManagerV2.merchantAuthData.isBusinessLicenseUpload = true
LifeAccountAuthDataManagerV2.merchantAuthData.businessLicenseUrl = imageUrl
}
}
......@@ -48,8 +48,8 @@ class LifeAccountEnterpriseAuthActivity : BaseActivity<ActivityLifeAccountEnterp
private val fragmentList: ArrayList<Fragment> by lazy {
arrayListOf(
LifeAccountBusinessLicenseAuthFragment(),
LifeAccountIDCardAuthFragment()
LifeAccountBusinessLicenseAuthFragmentV2(),
LifeAccountIDCardAuthFragmentV2()
)
}
......@@ -126,28 +126,28 @@ class LifeAccountEnterpriseAuthActivity : BaseActivity<ActivityLifeAccountEnterp
*/
fun checkNextCondition() {
if (viewBind.pvAuthProcess.curProcess == LifeAccountAuthProcessView.PROCESS_ENTERPRISE_QUALIFICATION
&& LifeAccountAuthDataManager.merchantAuthData.merchantType != 0
&& LifeAccountAuthDataManager.merchantAuthData.isBusinessLicenseUpload
&& !LifeAccountAuthDataManager.merchantAuthData.registrationName.isNullOrEmpty()
&& !LifeAccountAuthDataManager.merchantAuthData.registrationCode.isNullOrEmpty()
&& LifeAccountAuthDataManagerV2.merchantAuthData.merchantType != 0
&& LifeAccountAuthDataManagerV2.merchantAuthData.isBusinessLicenseUpload
&& !LifeAccountAuthDataManagerV2.merchantAuthData.registrationName.isNullOrEmpty()
&& !LifeAccountAuthDataManagerV2.merchantAuthData.registrationCode.isNullOrEmpty()
) {
viewBind.tvNext.alpha = 1f
viewBind.tvNext.isEnabled = true
} else if (viewBind.pvAuthProcess.curProcess == LifeAccountAuthProcessView.PROCESS_ENTERPRISE_MANAGER
&& LifeAccountAuthDataManager.personalAuthData.isAgreePrivacyAgreement
&& LifeAccountAuthDataManager.merchantAuthData.merchantType == Constant.TYPE_INDIVIDUAL_BUSINESS
&& LifeAccountAuthDataManager.personalAuthData.isFaceAuthPass
&& LifeAccountAuthDataManagerV2.personalAuthData.isAgreePrivacyAgreement
&& LifeAccountAuthDataManagerV2.merchantAuthData.merchantType == Constant.TYPE_INDIVIDUAL_BUSINESS
&& LifeAccountAuthDataManagerV2.personalAuthData.isFaceAuthPass
) {
viewBind.tvNext.alpha = 1f
viewBind.tvNext.isEnabled = true
} else if (viewBind.pvAuthProcess.curProcess == LifeAccountAuthProcessView.PROCESS_ENTERPRISE_MANAGER
&& LifeAccountAuthDataManager.personalAuthData.isAgreePrivacyAgreement
&& LifeAccountAuthDataManager.merchantAuthData.merchantType == Constant.TYPE_COMMON_ENTERPRISE
&& LifeAccountAuthDataManager.personalAuthData.isIDCardPortraitFaceUpload
&& LifeAccountAuthDataManager.personalAuthData.isIDCardNationalEmblemFaceUpload
&& !LifeAccountAuthDataManager.personalAuthData.realName.isNullOrEmpty()
&& !LifeAccountAuthDataManager.personalAuthData.idCardNum.isNullOrEmpty()
&& !LifeAccountAuthDataManager.personalAuthData.phoneNum.isNullOrEmpty()
&& LifeAccountAuthDataManagerV2.personalAuthData.isAgreePrivacyAgreement
&& LifeAccountAuthDataManagerV2.merchantAuthData.merchantType == Constant.TYPE_COMMON_ENTERPRISE
&& LifeAccountAuthDataManagerV2.personalAuthData.isIDCardPortraitFaceUpload
&& LifeAccountAuthDataManagerV2.personalAuthData.isIDCardNationalEmblemFaceUpload
&& !LifeAccountAuthDataManagerV2.personalAuthData.realName.isNullOrEmpty()
&& !LifeAccountAuthDataManagerV2.personalAuthData.idCardNum.isNullOrEmpty()
&& !LifeAccountAuthDataManagerV2.personalAuthData.phoneNum.isNullOrEmpty()
) {
viewBind.tvNext.alpha = 1f
viewBind.tvNext.isEnabled = true
......@@ -166,7 +166,7 @@ class LifeAccountEnterpriseAuthActivity : BaseActivity<ActivityLifeAccountEnterp
private fun initData() {
// 如果有已存在的认证流程则还原进度
authData?.let { LifeAccountAuthDataManager.generateMerchantAuthData(it) }
authData?.let { LifeAccountAuthDataManagerV2.generateMerchantAuthData(it) }
authData?.data_type?.let {
// 商户类型
val type = if (it == Constant.TYPE_INDIVIDUAL_BUSINESS) {
......@@ -178,7 +178,7 @@ class LifeAccountEnterpriseAuthActivity : BaseActivity<ActivityLifeAccountEnterp
lastSelectedMerchantType = type
}
val bundle = Bundle()
bundle.putSerializable(LifeAccountBusinessLicenseAuthFragment.EXTRA_AUTH_DATA, authData)
bundle.putSerializable(LifeAccountBusinessLicenseAuthFragmentV2.EXTRA_AUTH_DATA, authData)
fragmentList[0].arguments = bundle
switchFragment(fragmentList[0])
}
......@@ -217,18 +217,18 @@ class LifeAccountEnterpriseAuthActivity : BaseActivity<ActivityLifeAccountEnterp
* 避免在修改认证时出现数据异常
*/
private fun clearLifeAccountAuthData() {
LifeAccountAuthDataManager.merchantAuthData.merchantType = 0
LifeAccountAuthDataManager.merchantAuthData.authRecordId = -1
LifeAccountAuthDataManager.merchantAuthData.isBusinessLicenseUpload = false
LifeAccountAuthDataManager.merchantAuthData.registrationName = null
LifeAccountAuthDataManager.merchantAuthData.registrationCode = null
LifeAccountAuthDataManager.personalAuthData.isAgreePrivacyAgreement = false
LifeAccountAuthDataManager.personalAuthData.isFaceAuthPass = false
LifeAccountAuthDataManager.personalAuthData.isIDCardPortraitFaceUpload = false
LifeAccountAuthDataManager.personalAuthData.isIDCardNationalEmblemFaceUpload = false
LifeAccountAuthDataManager.personalAuthData.realName = null
LifeAccountAuthDataManager.personalAuthData.idCardNum = null
LifeAccountAuthDataManager.personalAuthData.phoneNum = null
LifeAccountAuthDataManagerV2.merchantAuthData.merchantType = 0
LifeAccountAuthDataManagerV2.merchantAuthData.authRecordId = -1
LifeAccountAuthDataManagerV2.merchantAuthData.isBusinessLicenseUpload = false
LifeAccountAuthDataManagerV2.merchantAuthData.registrationName = null
LifeAccountAuthDataManagerV2.merchantAuthData.registrationCode = null
LifeAccountAuthDataManagerV2.personalAuthData.isAgreePrivacyAgreement = false
LifeAccountAuthDataManagerV2.personalAuthData.isFaceAuthPass = false
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardPortraitFaceUpload = false
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardNationalEmblemFaceUpload = false
LifeAccountAuthDataManagerV2.personalAuthData.realName = null
LifeAccountAuthDataManagerV2.personalAuthData.idCardNum = null
LifeAccountAuthDataManagerV2.personalAuthData.phoneNum = null
}
override fun onClick(v: View?) {
......@@ -251,25 +251,25 @@ class LifeAccountEnterpriseAuthActivity : BaseActivity<ActivityLifeAccountEnterp
// 402接口 直接企业认证生活号id和审核id均不传 个人认证升级只传生活号id 认证修改只传审核id
ApiService.authBusinessLicenseCommit(
this,
LifeAccountAuthDataManager.generateAuthBusinessLicenseCommitRequest(
LifeAccountAuthDataManagerV2.generateAuthBusinessLicenseCommitRequest(
if (isAuthModify) null else lifeAccountId,
LifeAccountAuthDataManager.merchantAuthData.authRecordId
LifeAccountAuthDataManagerV2.merchantAuthData.authRecordId
)
)
} else if (viewBind.pvAuthProcess.curProcess == LifeAccountAuthProcessView.PROCESS_ENTERPRISE_MANAGER) {
// 406接口 直接企业认证和认证修改不传生活号id 个人认证升级传生活号id
if (LifeAccountAuthDataManager.merchantAuthData.merchantType == Constant.TYPE_INDIVIDUAL_BUSINESS) {
if (LifeAccountAuthDataManagerV2.merchantAuthData.merchantType == Constant.TYPE_INDIVIDUAL_BUSINESS) {
ApiService.authIndividualBusinessComplete(
this,
LifeAccountAuthDataManager.generateAuthIndividualBusinessCompleteRequest(
LifeAccountAuthDataManagerV2.generateAuthIndividualBusinessCompleteRequest(
if (isAuthModify) null else lifeAccountId
)
)
} else if (LifeAccountAuthDataManager.merchantAuthData.merchantType == Constant.TYPE_COMMON_ENTERPRISE) {
} else if (LifeAccountAuthDataManagerV2.merchantAuthData.merchantType == Constant.TYPE_COMMON_ENTERPRISE) {
// 403接口 直接企业认证和认证修改不传生活号id 个人认证升级传生活号id
ApiService.authEnterpriseLegalIdentityCommit(
this,
LifeAccountAuthDataManager.generateAuthEnterpriseLegalIdentityCommitRequest(
LifeAccountAuthDataManagerV2.generateAuthEnterpriseLegalIdentityCommitRequest(
if (isAuthModify) null else lifeAccountId
)
)
......@@ -287,12 +287,12 @@ class LifeAccountEnterpriseAuthActivity : BaseActivity<ActivityLifeAccountEnterp
// 回显选择的商户类型
viewBind.svMerchantType.setContentResult(data.item)
// 保存选择的商户类型
LifeAccountAuthDataManager.merchantAuthData.merchantType =
LifeAccountAuthDataManagerV2.merchantAuthData.merchantType =
if (data.item == Constant.ITEM_INDIVIDUAL_BUSINESSES) Constant.TYPE_INDIVIDUAL_BUSINESS else Constant.TYPE_COMMON_ENTERPRISE
// 第一次选择商户类型不清空营业执照信息
if (lastSelectedMerchantType != null) {
// 更换商户类型,清空营业执照图片、清空注册名称和注册码
(fragmentList[0] as LifeAccountBusinessLicenseAuthFragment).clearBusinessLicenseInfo()
(fragmentList[0] as LifeAccountBusinessLicenseAuthFragmentV2).clearBusinessLicenseInfo()
}
// 检查下一步条件
checkNextCondition()
......@@ -302,25 +302,25 @@ class LifeAccountEnterpriseAuthActivity : BaseActivity<ActivityLifeAccountEnterp
override fun authBusinessLicenseCommitSuccess(result: AuthBusinessLicenseCommitBean.Response?) {
// 营业执照提交成功
result?.enterprise_auth_record_id?.let { LifeAccountAuthDataManager.merchantAuthData.authRecordId = it }
result?.enterprise_auth_record_id?.let { LifeAccountAuthDataManagerV2.merchantAuthData.authRecordId = it }
viewBind.pvAuthProcess.setProcess(++viewBind.pvAuthProcess.curProcess)
viewBind.svMerchantType.enableSelect(false)
if (LifeAccountAuthDataManager.merchantAuthData.isAuthInfoModify) {
if (LifeAccountAuthDataManagerV2.merchantAuthData.isAuthInfoModify) {
// 认证信息修改 清空第二步回显数据
LifeAccountAuthDataManager.merchantAuthData.isAuthInfoModify = false
LifeAccountAuthDataManager.personalAuthData.isIDCardPortraitFaceUpload = false
LifeAccountAuthDataManager.personalAuthData.isIDCardNationalEmblemFaceUpload = false
LifeAccountAuthDataManager.personalAuthData.realName = null
LifeAccountAuthDataManager.personalAuthData.idCardNum = null
LifeAccountAuthDataManager.personalAuthData.isFaceAuthPass = false
LifeAccountAuthDataManager.personalAuthData.phoneNum = null
LifeAccountAuthDataManagerV2.merchantAuthData.isAuthInfoModify = false
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardPortraitFaceUpload = false
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardNationalEmblemFaceUpload = false
LifeAccountAuthDataManagerV2.personalAuthData.realName = null
LifeAccountAuthDataManagerV2.personalAuthData.idCardNum = null
LifeAccountAuthDataManagerV2.personalAuthData.isFaceAuthPass = false
LifeAccountAuthDataManagerV2.personalAuthData.phoneNum = null
fragmentList.removeAt(viewBind.pvAuthProcess.curProcess)
val fragment = LifeAccountIDCardAuthFragment()
val fragment = LifeAccountIDCardAuthFragmentV2()
fragmentList.add(viewBind.pvAuthProcess.curProcess, fragment)
val bundle = Bundle()
bundle.putInt(
Constant.TYPE_AUTH,
LifeAccountAuthDataManager.merchantAuthData.merchantType
LifeAccountAuthDataManagerV2.merchantAuthData.merchantType
)
fragment.arguments = bundle
} else {
......@@ -329,9 +329,9 @@ class LifeAccountEnterpriseAuthActivity : BaseActivity<ActivityLifeAccountEnterp
val bundle = Bundle()
bundle.putInt(
Constant.TYPE_AUTH,
LifeAccountAuthDataManager.merchantAuthData.merchantType
LifeAccountAuthDataManagerV2.merchantAuthData.merchantType
)
bundle.putSerializable(LifeAccountIDCardAuthFragment.EXTRA_AUTH_DATA, authData)
bundle.putSerializable(LifeAccountIDCardAuthFragmentV2.EXTRA_AUTH_DATA, authData)
fragment.arguments = bundle
}
switchFragment(fragmentList[viewBind.pvAuthProcess.curProcess])
......
package com.yidian.shenghuoquan.newscontent.ui.auth
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Bundle
import android.provider.MediaStore
import android.text.InputFilter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.tbruyelle.rxpermissions3.RxPermissions
import com.yidian.common.base.BaseFragment
import com.yidian.common.utils.ImageCompressUtils
import com.yidian.framework.mobile.insight.config.IdentityCardType
import com.yidian.framework.mobile.insight.config.ScreenOrientation
import com.yidian.framework.mobile.insight.faceid.LiveVerifyCallBack
import com.yidian.framework.mobile.insight.faceid.bean.LiveVerityResult
import com.yidian.framework.mobile.insight.identity.IdentityCardResult
import com.yidian.framework.mobile.insight.identity.IdentityCardVerifyCallBack
import com.yidian.framework.mobile.insight.manage.XInsight
import com.yidian.shenghuoquan.newscontent.R
import com.yidian.shenghuoquan.newscontent.adapter.BottomSelectAdapter
import com.yidian.shenghuoquan.newscontent.bean.BottomSelectBean
import com.yidian.shenghuoquan.newscontent.bean.HintOptionBean
import com.yidian.shenghuoquan.newscontent.constant.Constant
import com.yidian.shenghuoquan.newscontent.databinding.FragmentLifeAccountIdCardAuthBinding
import com.yidian.shenghuoquan.newscontent.http.ApiService
import com.yidian.shenghuoquan.newscontent.http.callback.IAuthEnterpriseLiveIdentityCallback
import com.yidian.shenghuoquan.newscontent.http.callback.IAuthIndividualBusinessIdentityCommitCallback
import com.yidian.shenghuoquan.newscontent.http.callback.IAuthLiveIdentityCallback
import com.yidian.shenghuoquan.newscontent.http.callback.IAuthPersonalCheckCallback
import com.yidian.shenghuoquan.newscontent.http.httpbean.*
import com.yidian.shenghuoquan.newscontent.ui.dialog.BottomSelectDialog
import com.yidian.shenghuoquan.newscontent.ui.dialog.HintDialog
import com.yidian.shenghuoquan.newscontent.utils.BitmapUtil
import com.yidian.shenghuoquan.newscontent.utils.FileUtil
import com.yidian.shenghuoquan.newscontent.utils.InputFilterUtil
import com.yidian.shenghuoquan.newscontent.widget.LifeAccountAuthIdentityInfoEditView
import com.yidian.shenghuoquan.newscontent.widget.LifeAccountAuthImageView
import com.yidian.shenghuoquan.newscontent.widget.LifeAccountFaceAuthView
import com.yidian.shenghuoquan.newscontent.widget.PrivacyAgreementView
import com.yidian.utils.ToastUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
/**
* author: yinjiacheng
* date: 7/15/21 5:32 PM
* description: 生活号身份证认证 接入XInsight
*/
// TODO: 7/15/21 活体检测临时数据删除
// TODO: 7/15/21 接入Luban压缩
class LifeAccountIDCardAuthFragmentV2 : BaseFragment<FragmentLifeAccountIdCardAuthBinding>(), View.OnClickListener,
BottomSelectAdapter.OnItemClickListener<BottomSelectBean>, IAuthPersonalCheckCallback, IAuthLiveIdentityCallback,
LifeAccountAuthImageView.OnLifeAccountAuthImageViewCallback,
LifeAccountAuthIdentityInfoEditView.OnLifeAccountAuthIdentityInfoEditViewCallback, IAuthIndividualBusinessIdentityCommitCallback,
IAuthEnterpriseLiveIdentityCallback, LifeAccountFaceAuthView.OnLifeAccountFaceAuthCallback,
PrivacyAgreementView.OnPrivacyAgreementCallback, LiveVerifyCallBack {
companion object {
// 身份证采集页面回传数据
private const val EXTRA_NAME_ID_CARD_IMAGE = "idcardimg_bitmap"
// 认证数据
const val EXTRA_AUTH_DATA = "authData"
}
/**
* 标识用户当前上传的是身份证人像面或身份证国徽面 帮助onActivityResult区分
*/
private var curFace: Int = -1
/**
* sdcard/Android/data/package/cache
*/
private val cachePath by lazy { activity?.externalCacheDir?.absolutePath }
/**
* 当前认证类型
*/
private var authType: Int? = null
override fun createViewBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentLifeAccountIdCardAuthBinding {
return FragmentLifeAccountIdCardAuthBinding.inflate(layoutInflater, container, false)
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (!hidden) {
// fragment显示
initView()
}
}
override fun init(savedInstanceState: Bundle?) {
super.init(savedInstanceState)
initView()
initListener()
initData()
}
private fun initView() {
authType = arguments?.getInt(Constant.TYPE_AUTH, Constant.TYPE_AUTH_PERSONAL)
?: Constant.TYPE_AUTH_PERSONAL
when (authType) {
Constant.TYPE_AUTH_PERSONAL -> {
viewBinding.evRealName.setTitleContent(resources.getString(R.string.real_name))
}
Constant.TYPE_AUTH_INDIVIDUAL_BUSINESS -> {
viewBinding.evRealName.setTitleContent(resources.getString(R.string.individual_business_manager_name))
}
Constant.TYPE_AUTH_COMMON_ENTERPRISE -> {
viewBinding.evRealName.setTitleContent(resources.getString(R.string.enterprise_legal_name))
}
}
viewBinding.evMobile.isVisible = authType == Constant.TYPE_AUTH_COMMON_ENTERPRISE
viewBinding.avFaceAuth.isVisible =
(authType == Constant.TYPE_AUTH_PERSONAL) or (authType == Constant.TYPE_AUTH_INDIVIDUAL_BUSINESS)
// 输入过滤
viewBinding.evRealName.setInputFilters(InputFilterUtil.getRealNameFilter(), InputFilter.LengthFilter(40))
// 身份证号码输入限制18位字符,数字+大小写x 小写x自动转大写X
viewBinding.evIdCardNumber.setInputFilters(InputFilterUtil.getIDCardNumberFilter(), InputFilter.LengthFilter(18), InputFilter.AllCaps())
viewBinding.evMobile.setInputFilters(InputFilterUtil.getPhoneNumberFilter(), InputFilter.LengthFilter(11))
}
private fun initListener() {
viewBinding.ivIdCardPortraitFace.setOnClickListener(this)
viewBinding.ivIdCardNationalEmblemFace.setOnClickListener(this)
viewBinding.ivIdCardPortraitFace.setOnLifeAccountAuthImageViewCallback(this)
viewBinding.ivIdCardNationalEmblemFace.setOnLifeAccountAuthImageViewCallback(this)
viewBinding.evRealName.setOnLifeAccountAuthIdentityInfoEditViewCallback(this)
viewBinding.evIdCardNumber.setOnLifeAccountAuthIdentityInfoEditViewCallback(this)
viewBinding.evMobile.setOnLifeAccountAuthIdentityInfoEditViewCallback(this)
viewBinding.avFaceAuth.setOnLifeAccountFaceAuthCallback(this)
viewBinding.avPrivacyAgreement.setOnPrivacyAgreementCallback(this)
}
private fun initData() {
// 还原认证数据
val authData = arguments?.getSerializable(EXTRA_AUTH_DATA) ?: return
if (authType == Constant.TYPE_AUTH_PERSONAL) {
fillPersonalAuthInfo(authData as AuthAuthenticationBean.Response)
} else {
fillEnterpriseAuthInfo(authData as AuthMerchantCheckBean.Response)
}
}
/**
* 个人认证流程中断,下次进入认证流程时还原进度
*/
private fun fillPersonalAuthInfo(data: AuthAuthenticationBean.Response) {
viewBinding.ivIdCardPortraitFace.addImage(data.front_id_card_url)
viewBinding.ivIdCardNationalEmblemFace.addImage(data.back_id_card_url)
viewBinding.evRealName.fillEditContent(data.full_name)
viewBinding.evIdCardNumber.fillEditContent(data.id_card)
if (data.live_identity_status == Constant.ALIVE_DETECT_SUCCESS) viewBinding.avFaceAuth.showFaceAuthResult(true)
// 如果人脸验证已通过 则直接锁定输入状态
if (data.live_identity_status == Constant.ALIVE_DETECT_SUCCESS) lockPersonalAuthInfo()
// 检查下一步条件
(activity as LifeAccountPersonalAuthActivity).checkNextCondition()
}
/**
* 企业认证流程中断,下次进入认证流程时还原进度
*/
private fun fillEnterpriseAuthInfo(data: AuthMerchantCheckBean.Response) {
viewBinding.ivIdCardPortraitFace.addImage(data.legal_front_id_card_url)
viewBinding.ivIdCardNationalEmblemFace.addImage(data.legal_back_id_card_url)
viewBinding.evRealName.fillEditContent(data.legal_person)
viewBinding.evIdCardNumber.fillEditContent(data.legal_id_card)
if (authType == Constant.TYPE_AUTH_INDIVIDUAL_BUSINESS) {
// 个体工商户
if (data.live_identity_status == Constant.ALIVE_DETECT_SUCCESS) viewBinding.avFaceAuth.showFaceAuthResult(true)
// 如果人脸验证已通过 则直接锁定输入状态
if (data.live_identity_status == Constant.ALIVE_DETECT_SUCCESS) lockEnterpriseAuthInfo()
} else {
// 普通企业
viewBinding.evMobile.fillEditContent(data.mobile)
}
// 检查下一步条件
(activity as LifeAccountEnterpriseAuthActivity).checkNextCondition()
}
/**
* 锁定当前输入状态
* 即完成人脸验证后无法修改身份证照片、姓名、身份证号、职业标签
*/
private fun lockPersonalAuthInfo() {
viewBinding.ivIdCardPortraitFace.disableModify()
viewBinding.ivIdCardNationalEmblemFace.disableModify()
viewBinding.evRealName.disableModify()
viewBinding.evIdCardNumber.disableModify()
if (authType == Constant.TYPE_AUTH_PERSONAL) {
(activity as LifeAccountPersonalAuthActivity).disableModifyOccupation()
}
}
/**
* 锁定当前输入状态
* 即完成人脸验证后无法修改身份证照片、姓名、身份证号
*/
private fun lockEnterpriseAuthInfo() {
viewBinding.ivIdCardPortraitFace.disableModify()
viewBinding.ivIdCardNationalEmblemFace.disableModify()
viewBinding.evRealName.disableModify()
viewBinding.evIdCardNumber.disableModify()
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.iv_id_card_portrait_face -> {
// 上传身份证人像面
curFace = Constant.ID_CARD_PORTRAIT_FACE
activity?.let {
BottomSelectDialog(
it,
this,
arrayListOf(
BottomSelectBean(Constant.ITEM_TAKE_PHOTO),
BottomSelectBean(Constant.ITEM_OPEN_ALBUM)
)
).show()
}
}
R.id.iv_id_card_national_emblem_face -> {
// 上传身份证国徽面
curFace = Constant.ID_CARD_NATIONAL_EMBLEM_FACE
activity?.let {
BottomSelectDialog(
it,
this,
arrayListOf(
BottomSelectBean(Constant.ITEM_TAKE_PHOTO),
BottomSelectBean(Constant.ITEM_OPEN_ALBUM)
)
).show()
}
}
}
}
override fun onItemClick(view: View, position: Int, data: BottomSelectBean) {
when (data.item) {
Constant.ITEM_TAKE_PHOTO -> {
// 判断camera权限
RxPermissions(this).request(Manifest.permission.CAMERA).subscribe {
if (it) {
// 调用XInsight进行身份证采集
XInsight.startIdentityCardOcr(
this,
ScreenOrientation.VERTICAL,
if (curFace == Constant.ID_CARD_PORTRAIT_FACE) IdentityCardType.PORTRAIT else IdentityCardType.NATIONAL_EMBLEM,
Constant.REQUEST_CODE_ID_CARD_CAPTURE
)
} else {
ToastUtil.showToast(activity, "请在设置里同意相关权限")
}
}
}
Constant.ITEM_OPEN_ALBUM -> {
// 打开系统相册
startActivityForResult(
Intent(Intent.ACTION_PICK).setDataAndType(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
"image/*"
),
Constant.REQUEST_CODE_OPEN_ALBUM
)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
Constant.REQUEST_CODE_OPEN_ALBUM -> {
if (curFace == Constant.ID_CARD_PORTRAIT_FACE) {
val bitmap = BitmapUtil.generateBitmapFromUri(
activity,
data?.data,
viewBinding.ivIdCardPortraitFace
)
viewBinding.ivIdCardPortraitFace.addImage(bitmap)
FileUtil.generateFileFromUri(
activity,
data?.data,
cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE
)
startIDCardVerify(Constant.ID_CARD_PORTRAIT_FACE)
} else {
val bitmap = BitmapUtil.generateBitmapFromUri(
activity,
data?.data,
viewBinding.ivIdCardNationalEmblemFace
)
viewBinding.ivIdCardNationalEmblemFace.addImage(bitmap)
FileUtil.generateFileFromUri(
activity,
data?.data,
cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE
)
startIDCardVerify(Constant.ID_CARD_NATIONAL_EMBLEM_FACE)
}
}
Constant.REQUEST_CODE_ID_CARD_CAPTURE -> {
val bytes = data?.getByteArrayExtra(EXTRA_NAME_ID_CARD_IMAGE)
if (curFace == Constant.ID_CARD_PORTRAIT_FACE) {
val bitmap = bytes?.size?.let {
BitmapFactory.decodeByteArray(
bytes, 0,
it
)
}
viewBinding.ivIdCardPortraitFace.addImage(bitmap)
BitmapUtil.generateFileFromBitmap(
bitmap,
100,
cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE
)
startIDCardVerify(Constant.ID_CARD_PORTRAIT_FACE)
} else {
val bitmap = bytes?.size?.let {
BitmapFactory.decodeByteArray(
bytes, 0,
it
)
}
viewBinding.ivIdCardNationalEmblemFace.addImage(bitmap)
BitmapUtil.generateFileFromBitmap(
bitmap,
100,
cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE
)
startIDCardVerify(Constant.ID_CARD_NATIONAL_EMBLEM_FACE)
}
}
}
}
}
/**
* 执行身份证识别
* @param face 身份证两面标识
*/
private fun startIDCardVerify(face: Int) {
lifecycleScope.launch {
// 压缩图片
val uploadFile = withContext(Dispatchers.IO) {
if (face == Constant.ID_CARD_PORTRAIT_FACE) {
activity?.let {
ImageCompressUtils.compress(
it,
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE),
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE),
Constant.COMPRESS_DEST_WIDTH, Constant.COMPRESS_DEST_HEIGHT, Constant.COMPRESS_DEST_QUALITY
)
}
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE)
} else {
activity?.let {
ImageCompressUtils.compress(
it,
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE),
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE),
Constant.COMPRESS_DEST_WIDTH, Constant.COMPRESS_DEST_HEIGHT, Constant.COMPRESS_DEST_QUALITY
)
}
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE)
}
}
XInsight.identityCardVerify(
if (face == Constant.ID_CARD_PORTRAIT_FACE) IdentityCardType.PORTRAIT else IdentityCardType.NATIONAL_EMBLEM,
uploadFile,
object : IdentityCardVerifyCallBack {
override fun onSucceed(identityCardResult: IdentityCardResult?, result: String?) {
activity?.runOnUiThread {
if (face == Constant.ID_CARD_PORTRAIT_FACE) {
identityCardResult?.let { LifeAccountAuthDataManagerV2.generateIDCardPortraitFaceData(it) }
// 回显OCR结果
viewBinding.evRealName.fillEditContent(identityCardResult?.posit?.name)
viewBinding.evIdCardNumber.fillEditContent(identityCardResult?.posit?.idcard_number)
// 此时身份证人像面已上传并OCR识别完成 删除本地临时存储文件
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE).apply {
if (exists()) delete()
}
// 检查下一步条件
if (authType == Constant.TYPE_AUTH_PERSONAL) {
activity?.let { (it as LifeAccountPersonalAuthActivity).checkNextCondition() }
} else {
activity?.let { (it as LifeAccountEnterpriseAuthActivity).checkNextCondition() }
}
} else {
identityCardResult?.let { LifeAccountAuthDataManagerV2.generateIDCardNationEmblemFaceData(it) }
// 此时身份证国徽面已上传并OCR识别完成 删除本地临时存储文件
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE).apply {
if (exists()) delete()
}
// 检查下一步条件
if (authType == Constant.TYPE_AUTH_PERSONAL) {
activity?.let { (it as LifeAccountPersonalAuthActivity).checkNextCondition() }
} else {
activity?.let { (it as LifeAccountEnterpriseAuthActivity).checkNextCondition() }
}
}
}
}
override fun onFail(code: Int, errMsg: String?, errDomain: String?) {
}
override fun onUploadTask(positImageUrl: String?, backImageUrl: String?) {
if (face == Constant.ID_CARD_PORTRAIT_FACE) {
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardPortraitFaceUpload =
true
LifeAccountAuthDataManagerV2.personalAuthData.idCardPortraitFaceUrl = positImageUrl
} else {
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardNationalEmblemFaceUpload =
true
LifeAccountAuthDataManagerV2.personalAuthData.idCardNationalEmblemFaceUrl = backImageUrl
}
}
})
}
}
override fun authPersonCheckSuccess() {
// 身份信息校验成功后才允许进行人脸验证
XInsight.startLiveVerify(
activity,
LifeAccountAuthDataManagerV2.personalAuthData.idCardNum,
LifeAccountAuthDataManagerV2.personalAuthData.realName,
Constant.TYPE_MEG_LIVE, this
)
}
override fun authPersonCheckFailure(code: Int?, message: String?) {
if (code == Constant.ERROR_CODE_PERSONAL_AUTH_INFO_DUPLICATE) {
// 身份证号已认证并且认证手机号不是当前手机号 弹窗提示
activity?.let {
HintDialog(
it, message.toString(),
arrayListOf(
HintOptionBean(resources.getString(R.string.change)),
HintOptionBean(resources.getString(R.string.login_again))
), it as LifeAccountPersonalAuthActivity
).setRightBtnColor(ContextCompat.getColor(it, R.color.color_FF1852F1)).show()
}
return
}
// 展示输入框下错误提示
when (message) {
Constant.ERROR_MESSAGE_REAL_NAME -> {
viewBinding.evRealName.showErrorTips(
resources.getText(R.string.id_card_name_error_tips) as String
)
viewBinding.evRealName.highLightEditContent()
}
Constant.ERROR_MESSAGE_ID_CARD_NUMBER -> {
viewBinding.evIdCardNumber.showErrorTips(resources.getText(R.string.id_card_number_error_tips) as String)
viewBinding.evIdCardNumber.highLightEditContent()
}
}
}
override fun authLiveIdentitySuccess() {
}
override fun authLiveIdentityFailure(message: String?) {
}
override fun onImageClear(from: LifeAccountAuthImageView) {
if (from == viewBinding.ivIdCardPortraitFace) {
// 清除照片时删除本地文件
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE).apply {
if (exists()) delete()
}
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardPortraitFaceUpload = false
viewBinding.evRealName.clearEditContent()
viewBinding.evIdCardNumber.clearEditContent()
LifeAccountAuthDataManagerV2.personalAuthData.realName = ""
LifeAccountAuthDataManagerV2.personalAuthData.idCardNum = ""
} else {
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE).apply {
if (exists()) delete()
}
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardNationalEmblemFaceUpload = false
}
// 检查下一步条件
if (authType == Constant.TYPE_AUTH_PERSONAL) {
(activity as LifeAccountPersonalAuthActivity).checkNextCondition()
} else {
(activity as LifeAccountEnterpriseAuthActivity).checkNextCondition()
}
}
override fun onTextClear(from: LifeAccountAuthIdentityInfoEditView) {
when (from) {
viewBinding.evRealName -> {
LifeAccountAuthDataManagerV2.personalAuthData.realName = ""
}
viewBinding.evIdCardNumber -> {
LifeAccountAuthDataManagerV2.personalAuthData.idCardNum = ""
}
viewBinding.evMobile -> {
LifeAccountAuthDataManagerV2.personalAuthData.phoneNum = ""
}
}
// 检查下一步条件
if (authType == Constant.TYPE_AUTH_PERSONAL) {
(activity as LifeAccountPersonalAuthActivity).checkNextCondition()
} else {
(activity as LifeAccountEnterpriseAuthActivity).checkNextCondition()
}
}
override fun onTextChange(from: LifeAccountAuthIdentityInfoEditView, text: String) {
when (from) {
viewBinding.evRealName -> {
LifeAccountAuthDataManagerV2.personalAuthData.realName = text
}
viewBinding.evIdCardNumber -> {
LifeAccountAuthDataManagerV2.personalAuthData.idCardNum = text
}
viewBinding.evMobile -> {
LifeAccountAuthDataManagerV2.personalAuthData.phoneNum = text
}
}
// 检查下一步条件
if (authType == Constant.TYPE_AUTH_PERSONAL) {
(activity as LifeAccountPersonalAuthActivity).checkNextCondition()
} else {
(activity as LifeAccountEnterpriseAuthActivity).checkNextCondition()
}
}
override fun onStartFaceAuth() {
// 判断是否满足条件进行活体检测
if (!checkFaceAuthCondition()) {
ToastUtil.showToast(activity, "请补全信息后重试")
return
}
// 判断camera权限
RxPermissions(this).request(Manifest.permission.CAMERA).subscribe {
if (it) {
when (authType) {
Constant.TYPE_AUTH_PERSONAL -> {
// 提交个人认证身份信息
ApiService.authPersonalCheck(
this,
LifeAccountAuthDataManagerV2.generateAuthPersonalCheckRequest()
)
}
Constant.TYPE_AUTH_INDIVIDUAL_BUSINESS -> {
// 提交企业认证身份信息
ApiService.authIndividualBusinessIdentityCommit(
this,
LifeAccountAuthDataManagerV2.generateAuthIndividualBusinessIdentityCommitRequest()
)
}
}
} else {
ToastUtil.showToast(activity, "请在设置里同意相关权限")
}
}
}
override fun authIndividualBusinessIdentityCommitSuccess() {
// 身份信息校验成功后才允许进行人脸验证
XInsight.startLiveVerify(
activity,
LifeAccountAuthDataManagerV2.personalAuthData.idCardNum,
LifeAccountAuthDataManagerV2.personalAuthData.realName,
Constant.TYPE_MEG_LIVE, this
)
}
override fun authIndividualBusinessIdentityCommitFailure(message: String?) {
// 展示输入框下错误提示
when (message) {
Constant.ERROR_MESSAGE_REAL_NAME -> {
viewBinding.evRealName.showErrorTips(
resources.getText(R.string.id_card_name_error_tips) as String
)
viewBinding.evRealName.highLightEditContent()
}
Constant.ERROR_MESSAGE_ID_CARD_NUMBER -> {
viewBinding.evIdCardNumber.showErrorTips(resources.getText(R.string.id_card_number_error_tips) as String)
viewBinding.evIdCardNumber.highLightEditContent()
}
}
}
override fun authEnterpriseLiveIdentitySuccess() {
}
override fun authEnterpriseLiveIdentityFailure(message: String?) {
}
/**
* 本地校验是否可以进行人脸验证
* 必须同时满足真实姓名已填、身份证号已填、身份证两面已上传、职业标签已选(只有个人认证需要)
*/
private fun checkFaceAuthCondition(): Boolean {
if (authType == Constant.TYPE_AUTH_PERSONAL) {
return !LifeAccountAuthDataManagerV2.personalAuthData.occupation.isNullOrEmpty()
&& !LifeAccountAuthDataManagerV2.personalAuthData.realName.isNullOrEmpty()
&& !LifeAccountAuthDataManagerV2.personalAuthData.idCardNum.isNullOrEmpty()
&& LifeAccountAuthDataManagerV2.personalAuthData.isIDCardPortraitFaceUpload
&& LifeAccountAuthDataManagerV2.personalAuthData.isIDCardNationalEmblemFaceUpload
}
return !LifeAccountAuthDataManagerV2.personalAuthData.realName.isNullOrEmpty()
&& !LifeAccountAuthDataManagerV2.personalAuthData.idCardNum.isNullOrEmpty()
&& LifeAccountAuthDataManagerV2.personalAuthData.isIDCardPortraitFaceUpload
&& LifeAccountAuthDataManagerV2.personalAuthData.isIDCardNationalEmblemFaceUpload
}
override fun onCheckPrivacyAgreement(isCheck: Boolean) {
LifeAccountAuthDataManagerV2.personalAuthData.isAgreePrivacyAgreement = isCheck
// 检查下一步条件
if (authType == Constant.TYPE_AUTH_PERSONAL) {
(activity as LifeAccountPersonalAuthActivity).checkNextCondition()
} else {
(activity as LifeAccountEnterpriseAuthActivity).checkNextCondition()
}
}
// 活体认证回调
override fun onSucceed(liveVerityResult: LiveVerityResult?, result: String?) {
activity?.runOnUiThread {
if (liveVerityResult?.verify_result == true) {
// 保存人脸验证结果
LifeAccountAuthDataManagerV2.personalAuthData.isFaceAuthPass = true
// 人脸验证通过UI
viewBinding.avFaceAuth.showFaceAuthResult(true)
// 人脸验证通过锁定信息
lockPersonalAuthInfo()
// 回调server 保存活体识别认证状态
if (authType == Constant.TYPE_AUTH_PERSONAL) {
ApiService.authLiveIdentity(this@LifeAccountIDCardAuthFragmentV2)
// 检查下一步条件
activity?.let { (it as LifeAccountPersonalAuthActivity).checkNextCondition() }
} else {
ApiService.authEnterpriseLiveIdentity(
this@LifeAccountIDCardAuthFragmentV2,
hashMapOf("record_id" to LifeAccountAuthDataManagerV2.merchantAuthData.authRecordId.toString())
)
// 检查下一步条件
activity?.let { (it as LifeAccountEnterpriseAuthActivity).checkNextCondition() }
}
}
}
}
override fun onFail(code: Int, errMsg: String?, errDomain: String?) {
activity?.runOnUiThread {
LifeAccountAuthDataManagerV2.personalAuthData.isFaceAuthPass = false
viewBinding.avFaceAuth.showFaceAuthResult(false)
}
}
override fun onUploadTask(imageUrl: String?) {
}
}
......@@ -41,7 +41,7 @@ class LifeAccountPersonalAuthActivity :
/**
* 当前fragment
*/
private lateinit var curFragment: LifeAccountIDCardAuthFragment
private lateinit var curFragment: LifeAccountIDCardAuthFragmentV2
/**
* 认证数据 还原认证流程用
......@@ -92,11 +92,11 @@ class LifeAccountPersonalAuthActivity :
private fun initData() {
// 如果有已存在的认证流程则还原进度
authData?.let { LifeAccountAuthDataManager.generatePersonalAuthData(it) }
authData?.let { LifeAccountAuthDataManagerV2.generatePersonalAuthData(it) }
authData?.occupation?.let { viewBind.svOccupation.setContentResult(it) }
curFragment = LifeAccountIDCardAuthFragment()
curFragment = LifeAccountIDCardAuthFragmentV2()
val bundle = Bundle()
bundle.putSerializable(LifeAccountIDCardAuthFragment.EXTRA_AUTH_DATA, authData)
bundle.putSerializable(LifeAccountIDCardAuthFragmentV2.EXTRA_AUTH_DATA, authData)
curFragment.arguments = bundle
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, curFragment).commit()
......@@ -106,9 +106,9 @@ class LifeAccountPersonalAuthActivity :
* 检查是否可以进行下一步
*/
fun checkNextCondition() {
if (LifeAccountAuthDataManager.personalAuthData.isAgreePrivacyAgreement
&& !LifeAccountAuthDataManager.personalAuthData.occupation.isNullOrEmpty()
&& LifeAccountAuthDataManager.personalAuthData.isFaceAuthPass
if (LifeAccountAuthDataManagerV2.personalAuthData.isAgreePrivacyAgreement
&& !LifeAccountAuthDataManagerV2.personalAuthData.occupation.isNullOrEmpty()
&& LifeAccountAuthDataManagerV2.personalAuthData.isFaceAuthPass
) {
viewBind.tvNext.alpha = 1f
viewBind.tvNext.isEnabled = true
......@@ -130,14 +130,14 @@ class LifeAccountPersonalAuthActivity :
* 避免在升级企业认证时出现数据异常
*/
private fun clearLifeAccountAuthData() {
LifeAccountAuthDataManager.personalAuthData.isAgreePrivacyAgreement = false
LifeAccountAuthDataManager.personalAuthData.occupation = null
LifeAccountAuthDataManager.personalAuthData.isFaceAuthPass = false
LifeAccountAuthDataManager.personalAuthData.realName = null
LifeAccountAuthDataManager.personalAuthData.idCardNum = null
LifeAccountAuthDataManager.personalAuthData.occupation = null
LifeAccountAuthDataManager.personalAuthData.isIDCardPortraitFaceUpload = false
LifeAccountAuthDataManager.personalAuthData.isIDCardNationalEmblemFaceUpload = false
LifeAccountAuthDataManagerV2.personalAuthData.isAgreePrivacyAgreement = false
LifeAccountAuthDataManagerV2.personalAuthData.occupation = null
LifeAccountAuthDataManagerV2.personalAuthData.isFaceAuthPass = false
LifeAccountAuthDataManagerV2.personalAuthData.realName = null
LifeAccountAuthDataManagerV2.personalAuthData.idCardNum = null
LifeAccountAuthDataManagerV2.personalAuthData.occupation = null
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardPortraitFaceUpload = false
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardNationalEmblemFaceUpload = false
}
override fun onClick(v: View?) {
......@@ -192,7 +192,7 @@ class LifeAccountPersonalAuthActivity :
when (event?.name) {
XEventConfig.CHOOSE_CAREER -> {
val selectedCareer = event.body as String
LifeAccountAuthDataManager.personalAuthData.occupation = selectedCareer
LifeAccountAuthDataManagerV2.personalAuthData.occupation = selectedCareer
viewBind.svOccupation.setContentResult(selectedCareer)
checkNextCondition()
}
......@@ -206,7 +206,7 @@ class LifeAccountPersonalAuthActivity :
// 清空职业标签回显
viewBind.svOccupation.clearContentResult()
// 清空身份信息页 直接替换fragment
val targetFragment = LifeAccountIDCardAuthFragment()
val targetFragment = LifeAccountIDCardAuthFragmentV2()
supportFragmentManager.beginTransaction().remove(curFragment).add(R.id.fragment_container, targetFragment).commit()
curFragment = targetFragment
}
......
......@@ -16,58 +16,60 @@ import java.io.FileOutputStream
*/
object BitmapUtil {
/**
* 从Uri生成Bitmap
* @param container bitmap容器
*/
fun generateBitmapFromUri(context: Context?, uri: Uri?, container: View): Bitmap? {
if (uri == null) return null
val parcelFileDescriptor = context?.contentResolver?.openFileDescriptor(uri, "r")
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFileDescriptor(parcelFileDescriptor?.fileDescriptor, null, options)
// 尺寸压缩
val sourceWidth = options.outWidth
val sourceHeight = options.outHeight
var inSampleSize = 1
if (sourceWidth > container.width || sourceHeight > container.height) {
val halfWidth = sourceWidth / 2
val halfHeight = sourceHeight / 2
// 压缩后的尺寸与所需的尺寸进行比较
while (halfWidth / inSampleSize >= container.width && halfHeight / inSampleSize >= container.height) {
inSampleSize *= 2
}
}
options.inSampleSize = inSampleSize
options.inJustDecodeBounds = false
val bitmap =
BitmapFactory.decodeFileDescriptor(parcelFileDescriptor?.fileDescriptor, null, options)
parcelFileDescriptor?.close()
return bitmap
/**
* 从Uri生成Bitmap
* @param container bitmap容器
*/
fun generateBitmapFromUri(context: Context?, uri: Uri?, container: View): Bitmap? {
if (uri == null) return null
val parcelFileDescriptor = context?.contentResolver?.openFileDescriptor(uri, "r")
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFileDescriptor(parcelFileDescriptor?.fileDescriptor, null, options)
// 尺寸压缩
val sourceWidth = options.outWidth
val sourceHeight = options.outHeight
var inSampleSize = 1
if (sourceWidth > container.width || sourceHeight > container.height) {
val halfWidth = sourceWidth / 2
val halfHeight = sourceHeight / 2
// 压缩后的尺寸与所需的尺寸进行比较
while (halfWidth / inSampleSize >= container.width && halfHeight / inSampleSize >= container.height) {
inSampleSize *= 2
}
}
options.inSampleSize = inSampleSize
options.inJustDecodeBounds = false
val bitmap =
BitmapFactory.decodeFileDescriptor(parcelFileDescriptor?.fileDescriptor, null, options)
parcelFileDescriptor?.close()
return bitmap
}
/**
* 从Bitmap生成File
* @param quality 压缩质量
* @param destPath 目标文件全路径
*/
fun generateFileFromBitmap(bitmap: Bitmap?, quality: Int, destPath: String) {
val file = File(destPath)
if (file.exists()) file.delete()
file.parentFile?.mkdirs()
file.createNewFile()
bitmap?.compress(Bitmap.CompressFormat.JPEG, quality, FileOutputStream(destPath))
}
/**
* 从Bitmap生成File
* @param quality 压缩质量
* @param destPath 目标文件全路径
*/
fun generateFileFromBitmap(bitmap: Bitmap?, quality: Int, destPath: String) {
val file = File(destPath)
if (file.exists()) file.delete()
if (file.parentFile?.exists() == false) file.parentFile?.mkdirs()
file.createNewFile()
val outputStream = FileOutputStream(destPath)
bitmap?.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
outputStream.close()
}
/**
* 图片压缩
* @param path 将要压缩的图片圈路径,同时也是压缩完成后的图片 即覆盖原图片
* @param quality 压缩质量
*/
fun compressImage(path: String, quality: Int) {
if (!File(path).exists()) throw FileNotFoundException("compress file not exist!")
BitmapFactory.decodeFile(path)
.compress(Bitmap.CompressFormat.JPEG, quality, FileOutputStream(path))
}
/**
* 图片压缩
* @param path 将要压缩的图片圈路径,同时也是压缩完成后的图片 即覆盖原图片
* @param quality 压缩质量
*/
fun compressImage(path: String, quality: Int) {
if (!File(path).exists()) throw FileNotFoundException("compress file not exist!")
BitmapFactory.decodeFile(path)
.compress(Bitmap.CompressFormat.JPEG, quality, FileOutputStream(path))
}
}
\ No newline at end of file
}
......@@ -13,44 +13,50 @@ import java.io.FileOutputStream
object FileUtil {
/**
* 从Uri生成File
* @param path 生成文件的全路径
*/
fun generateFileFromUri(context: Context?, uri: Uri?, path: String) {
if (uri != null) {
val file = File(path)
if (file.exists()) file.delete()
file.parentFile?.mkdirs()
file.createNewFile()
context?.contentResolver?.openInputStream(uri)?.copyTo(FileOutputStream(path))
}
/**
* 从Uri生成File
* @param path 生成文件的全路径
*/
fun generateFileFromUri(context: Context?, uri: Uri?, path: String) {
if (uri != null) {
val file = File(path)
if (file.exists()) file.delete()
if (file.parentFile?.exists() == false) file.parentFile?.mkdirs()
file.createNewFile()
val inputStream = context?.contentResolver?.openInputStream(uri)
val outputStream = FileOutputStream(path)
inputStream?.copyTo(outputStream)
inputStream?.close()
outputStream.close()
}
}
/**
* 从assets中复制文件到指定路径
* @param assetFileName assets中文件名
* @param destPath 目标路径
*/
fun copyFileFromAssets(context: Context?, assetFileName: String, destPath: String) {
val file = File(destPath)
if (file.exists()) file.delete()
file.parentFile?.mkdirs()
file.createNewFile()
context?.assets?.open(assetFileName)?.copyTo(FileOutputStream(file))
}
/**
* 从assets中复制文件到指定路径
* @param assetFileName assets中文件名
* @param destPath 目标路径
*/
fun copyFileFromAssets(context: Context?, assetFileName: String, destPath: String) {
val file = File(destPath)
if (file.exists()) file.delete()
if (file.parentFile?.exists() == false) file.parentFile?.mkdirs()
file.createNewFile()
val outputStream = FileOutputStream(file)
context?.assets?.open(assetFileName)?.copyTo(outputStream)
outputStream.close()
}
/**
* 从String生成File
* @param data 待写入的String
* @param destPath 目标路径
*/
fun generateFileFromString(data: String?, destPath: String) {
if (data.isNullOrEmpty()) return
val file = File(destPath)
if (file.exists()) file.delete()
file.parentFile?.mkdirs()
file.createNewFile()
File(destPath).writeText(data)
}
}
\ No newline at end of file
/**
* 从String生成File
* @param data 待写入的String
* @param destPath 目标路径
*/
fun generateFileFromString(data: String?, destPath: String) {
if (data.isNullOrEmpty()) return
val file = File(destPath)
if (file.exists()) file.delete()
if (file.parentFile?.exists() == false) file.parentFile?.mkdirs()
file.createNewFile()
File(destPath).writeText(data)
}
}
......@@ -397,3 +397,6 @@ public static final int *;
-keep class com.amap.api.navi.**{*;}
-keep class com.autonavi.**{*;}
#XInsight
-keep public class com.megvii.**{*;}
......@@ -20,6 +20,10 @@ import com.yidian.common.services.XPageService
import com.yidian.common.services.http.MBHttpReqService
import com.yidian.common.utils.UMConfigUtils
import com.yidian.commonutil.CommonUtilLifecycleModule
import com.yidian.framework.mobile.insight.config.ServerUrlConfig
import com.yidian.framework.mobile.insight.config.XDiamondProvide
import com.yidian.framework.mobile.insight.manage.XInsight
import com.yidian.framework.mobile.insight.manage.XInsightConfig
import com.yidian.framework.mobile.xdiamond.SecretUtil
import com.yidian.http.ServiceFactory
import com.yidian.news.util.ProcessUtil
......@@ -57,10 +61,12 @@ class YdSubwayApplication : YdBaseApplication() {
loadService()
registerActivityLifecycleCallbacks(activityLifecycle)
// 初始化磐石SDK
SecretUtil.init(BuildConfig.IS_ENCRYPT_DEBUG_KEY)
SecretUtil.init(BuildConfig.IS_ENCRYPT_DEBUG_KEY, BuildConfig.DEBUG)
// 初始化App升级
initAppUpgrade()
initSmartRefreshLayout()
// 初始化慧眼
initXInsight()
Timber.tag("启动").e("onCreate 执行完成")
}
}
......@@ -249,4 +255,50 @@ class YdSubwayApplication : YdBaseApplication() {
})
}
/**
* 慧眼
*/
private fun initXInsight() {
val config = XInsightConfig()
config.cv = AppConfig.cv
config.device_finger = FtDeviceFingerManager.getDeviceFinger()
config.distribution = AppConfig.distribution
config.isEncryption = AppConfig.IS_ENCRYPT
config.version = AppConfig.version
config.serverUrlConfig = ServerUrlConfig.DEV_SERVER_URL
val provide = object : XDiamondProvide {
override fun sign(str: String?): String? {
if (str == null) return null
return SecretUtil.sign(str)
}
override fun rsaEncrypt(str: String?): String? {
if (str == null) return null
return SecretUtil.rsaEncrypt(str)
}
override fun rsaDecrypt(str: String?): String? {
if (str == null) return null
return SecretUtil.rsaDecrypt(str)
}
override fun aesEncrypt(str: String?): String? {
if (str == null) return null
return SecretUtil.aesEncrypt(str)
}
override fun aesDecrypt(str: String?): String? {
if (str == null) return null
return SecretUtil.aesDecrypt(str)
}
override fun verifySign(src: String?, sign: String?): Boolean {
if (src == null || sign == null) return false
return SecretUtil.verifySign(src, sign)
}
}
XInsight.init(this, AppConfig.appid, BuildConfig.DEBUG, config, provide)
}
}
......@@ -128,7 +128,9 @@ ext.dependencies = [
'io.reactivex.rxjava3:rxjava:3.0.9',
'com.yidian.framework.mobile:ydhttp:1.1.3-SNAPSHOT',
'com.yidian.framework.mobile:ydutils:1.0.1-SNAPSHOT',
'com.yidian.framework.mobile:xdiamond:1.0.6',
'com.yidian.framework.mobile:xdiamond:1.0.7',
// 慧眼
'com.yidian.framework.mobile.xinsight:xinsight:1.1.5',
'com.loopj.android:android-async-http:1.4.9',
// 屏幕适配
......@@ -136,7 +138,9 @@ ext.dependencies = [
//https://github.com/CymChad/BaseRecyclerViewAdapterHelper
'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4',
// PictureSelector2.0 https://github.com/LuckSiege/PictureSelector
'io.github.lucksiege:pictureselector:v2.7.3-rc05'
'io.github.lucksiege:pictureselector:v2.7.3-rc05',
// Compressor https://github.com/zetbaitsu/Compressor
'id.zelory:compressor:3.0.1'
],
other : [
':CommonLib:Common'
......
apply plugin: 'com.android.library'
android {
compileSdkVersion build_versions.compileSdkVersion
defaultConfig {
minSdkVersion build_versions.minSdkVersion
targetSdkVersion build_versions.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
android {
lintOptions {
abortOnError false
}
}
}
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'com.android.support:appcompat-v7:25+'
implementation 'com.android.support.constraint:constraint-layout:2.0.1'
}
package com.megvii.demo;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.megvii.idcard.demo", appContext.getPackageName());
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.megvii.idcard.quality">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true">
<activity
android:name="com.megvii.demo.activity.IDCardDetectActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTask"
android:screenOrientation="portrait" />
</application>
</manifest>
\ No newline at end of file
package com.megvii.demo.activity;
import android.Manifest;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.TextureView;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.megvii.demo.utils.CommonUtils;
import com.megvii.demo.utils.Configuration;
import com.megvii.demo.utils.ICamera;
import com.megvii.demo.utils.ModelUtil;
import com.megvii.demo.utils.RotaterUtil;
import com.megvii.demo.utils.StatusBarCompat;
import com.megvii.demo.view.IDCardGuide;
import com.megvii.demo.view.IDCardGuideH;
import com.megvii.idcard.quality.R;
import com.megvii.idcardquality.IDCardQualityAssessment;
import com.megvii.idcardquality.IDCardQualityResult;
import com.megvii.idcardquality.bean.IDCardAttr;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import static android.os.Build.VERSION_CODES.M;
public class IDCardDetectActivity extends Activity implements TextureView.SurfaceTextureListener, Camera.PreviewCallback, View.OnClickListener {
private TextureView mTextureView;
private RelativeLayout mRlMegviiTips, mTitleBar, mRlMegviiGoBack, mRlMegviiGoBackH, mTitleBarH;
private TextView mTvMegviiTips, mTvMegviiTipsH;
private RelativeLayout mRelativeLayout;
private IDCardGuide mIdcardGuide;
private IDCardGuideH mIdcardGuideH;
private boolean mIsVertical;
private int mCardType = 0;
private IDCardAttr.IDCardSide mSide;
private ICamera mICamera;
private BlockingQueue<byte[]> mFrameDataQueue;
private boolean mHasSurface = false;
private DecodeThread mDecoder = null;
private IDCardQualityAssessment mIdCardQualityAssessment = null;
private Rect mRoi;
private ImageView mImageView, mIvBottomLogoH;
private boolean isCanDetected = false;//是否可以检测,5s超时后停止检测 TODO
private boolean isDetectFinished = false;
private IDCardQualityResult mQualityResult;
private ImageView mIbAnimalBreathView;
private ImageView mIbAnimalOneView;
private ImageView mIvPeopleIconlightView;
private ImageView mIvChinaIconLightView;
private RectF mRectScreen;
private TextView mTvTipText;
private ObjectAnimator mIDcardAlphaAnimation;
private ObjectAnimator mPeopleIconAlphaAnimation;
private ObjectAnimator mChinaIconAlphaAnimation;
private ImageView mIvIconTickAll;
private TextView mToastTitle;
private ImageView mToastTip;
private RelativeLayout mLayoutToast;
private int FIRST_RECT = 1;
private int SENCOND_RECT = 2;
private int rectType = FIRST_RECT;//第几面
private byte[] mImgData = null;
private int mImageWidth;
private int mImageHeight;
private long mBeginTime;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mIsVertical = Configuration.getIsVertical(this);
if (!mIsVertical) {
setTheme(R.style.idcard_cn_FullScreenTheme);
CommonUtils.toggleHideyBar(this);
} else {
StatusBarCompat.compat(this, getResources().getColor(R.color.idcard_cn_title_bar_bg_color));
}
setContentView(R.layout.activity_detect);
initView();
}
private void initView() {
mCardType = Configuration.getCardType(this);//1代表人像面,2代表国徽面检测
mIsVertical = Configuration.getIsVertical(this);//横竖屏
mRelativeLayout = (RelativeLayout) findViewById(R.id.rl_idcard_cn_root_view);
mRlMegviiGoBack = (RelativeLayout) findViewById(R.id.rl_megvii_idcard_cn_goback);
mRlMegviiGoBackH = (RelativeLayout) findViewById(R.id.rl_megvii_idcard_cn_goback_h);
mImageView = (ImageView) findViewById(R.id.iv_idcard_cn_image);
mTextureView = (TextureView) findViewById(R.id.idcardscan_cn_layout_surface);
mIvBottomLogoH = (ImageView) findViewById(R.id.iv_idcard_cn_bottom_logo_h);
mRlMegviiTips = (RelativeLayout) findViewById(R.id.rl_megvii_idcard_cn_tips);
mTitleBar = (RelativeLayout) findViewById(R.id.in_idcard_cn_title_bar);
mTitleBarH = (RelativeLayout) findViewById(R.id.rl_megvii_idcard_cn_title_bar_h);
mTvMegviiTips = (TextView) findViewById(R.id.tv_megvii_idcard_cn_tips);
mTvMegviiTipsH = (TextView) findViewById(R.id.tv_megvii_idcard_cn_tips_h);
mIvPeopleIconlightView = (ImageView) findViewById(R.id.iv_people_light_icon);
mIvChinaIconLightView = (ImageView) findViewById(R.id.iv_china_light_icon);
mIbAnimalBreathView = (ImageView) findViewById(R.id.ib_animal_breath_view);
mIbAnimalOneView = (ImageView) findViewById(R.id.ib_animal_one_view);
mToastTitle = (TextView) findViewById(R.id.toast_tv);
mToastTip = (ImageView) findViewById(R.id.iv_auth_toast_tip);
mLayoutToast = (RelativeLayout) findViewById(R.id.layout_toast);
mTvTipText = (TextView) findViewById(R.id.tv_tip_text);
mIvIconTickAll = (ImageView) findViewById(R.id.iv_icon_tick_all);
mIdcardGuide = (IDCardGuide) findViewById(R.id.idcardscan_cn_layout_guide);
mIdcardGuideH = (IDCardGuideH) findViewById(R.id.idcardscan_cn_layout_guide_h);
mIdcardGuide.setOnClickListener(this);
mIdcardGuideH.setOnClickListener(this);
mRlMegviiGoBack.setOnClickListener(this);
mRlMegviiGoBackH.setOnClickListener(this);
mTextureView.setOnClickListener(this);
mRelativeLayout.setOnClickListener(this);
mTextureView.setSurfaceTextureListener(this);
mIbAnimalBreathView.setBackgroundResource(R.mipmap.bg_sfz_light);
mIbAnimalOneView.setVisibility(View.VISIBLE);
mTvTipText.setText(getResources().getString(R.string.tip_rect_first_tip));
if (mCardType == 1) {
mSide = IDCardAttr.IDCardSide.IDCARD_SIDE_FRONT;
mIbAnimalOneView.setBackgroundResource(R.mipmap.bg_people_icon);
if (mIsVertical) {
mTvMegviiTips.setText(getResources().getString(R.string.idcard_cn_tips_face));
} else {
mTvMegviiTipsH.setText(getResources().getString(R.string.idcard_cn_tips_face));
}
} else {
mSide = IDCardAttr.IDCardSide.IDCARD_SIDE_BACK;
mIbAnimalOneView.setBackgroundResource(R.mipmap.bg_china_icon);
if (mIsVertical) {
mTvMegviiTips.setText(getResources().getString(R.string.idcard_cn_tips_emblem));
} else {
mTvMegviiTipsH.setText(getResources().getString(R.string.idcard_cn_tips_emblem));
}
}
if (mIsVertical) {
mRelativeLayout.setBackgroundColor(Color.WHITE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mIdcardGuide.setVisibility(View.VISIBLE);
mIdcardGuideH.setVisibility(View.GONE);
mTitleBar.setVisibility(View.VISIBLE);
mTitleBarH.setVisibility(View.GONE);
mRlMegviiTips.setVisibility(View.VISIBLE);
mIdcardGuide.setCardSide(mSide);
} else {//横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mRelativeLayout.setBackgroundColor(Color.BLACK);
mIvBottomLogoH.setVisibility(View.VISIBLE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mIdcardGuide.setVisibility(View.GONE);
mIdcardGuideH.setVisibility(View.VISIBLE);
mTitleBar.setVisibility(View.GONE);
mTitleBarH.setVisibility(View.VISIBLE);
mRlMegviiTips.setVisibility(View.GONE);
mIdcardGuideH.setCardSide(mSide);
}
if (Build.VERSION.SDK_INT >= M) {
if (checkSelfPermission(Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "没有摄像机权限", Toast.LENGTH_SHORT).show();
doFinish();
}
}
initSdk();
}
private void initSdk() {
mICamera = new ICamera(mIsVertical);
mFrameDataQueue = new LinkedBlockingDeque<byte[]>(1);
//初始化
mIdCardQualityAssessment = new IDCardQualityAssessment.Builder()
.setIsIgnoreShadow(false)// 不忽略阴影
.setIsIgnoreHighlight(false)//不忽略光斑
.build();
//必须先做授权判断
//加载模型,加载该方法之前,一定要先调用授权!!否则会crash!!!注意
boolean initSuccess = mIdCardQualityAssessment.init(this, ModelUtil.readModel(this, R.raw.meg_idcard));
if (!initSuccess) {
Toast.makeText(this, "检测器初始化失败", Toast.LENGTH_SHORT).show();
doFinish();
}
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (isCanDetected) {
mFrameDataQueue.offer(data);
}
}
private void doPreview() {
if (!mHasSurface) {
return;
} else {
mICamera.startPreview(mTextureView.getSurfaceTexture());
}
}
/**
* 加载完毕后取得身份证的坐标,计算动画的位置
*/
private void initIdCardRect() {
int imageWidth = mICamera.cameraWidth;
int imageHeight = mICamera.cameraHeight;
if (mIsVertical) {
imageWidth = mICamera.cameraHeight;
imageHeight = mICamera.cameraWidth;
}
RectF rectF;
if (mIsVertical) {
rectF = mIdcardGuide.getPosition(rectType);
} else {
rectF = mIdcardGuideH.getPosition(rectType);
}
mRoi = new Rect();
mRoi.left = (int) (rectF.left * imageWidth);
mRoi.top = (int) (rectF.top * imageHeight);
mRoi.right = (int) (rectF.right * imageWidth);
mRoi.bottom = (int) (rectF.bottom * imageHeight);
if (!isEven01(mRoi.left))
mRoi.left = mRoi.left + 1;
if (!isEven01(mRoi.top))
mRoi.top = mRoi.top + 1;
if (!isEven01(mRoi.right))
mRoi.right = mRoi.right - 1;
if (!isEven01(mRoi.bottom))
mRoi.bottom = mRoi.bottom - 1;
//=========animation
if (mIsVertical) {
mRectScreen = mIdcardGuide.getScreenPosition(rectType);
} else {
mRectScreen = mIdcardGuideH.getScreenPosition(rectType);
}
int width = (int) (mRectScreen.right - mRectScreen.left);
int height = (int) (mRectScreen.bottom - mRectScreen.top);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(mIbAnimalOneView.getLayoutParams());
int offset = (int) getResources().getDimension(R.dimen.idcard_checktrue_rect_big_offset);
layoutParams.width = width + offset;
layoutParams.height = height + offset;
layoutParams.topMargin = (int) mRectScreen.top - offset / 2;
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
mIbAnimalBreathView.setLayoutParams(layoutParams);
mIbAnimalOneView.setLayoutParams(layoutParams);
mIvChinaIconLightView.setLayoutParams(layoutParams);
mIvPeopleIconlightView.setLayoutParams(layoutParams);
//--tip textview
RelativeLayout.LayoutParams textlayoutParams = new RelativeLayout.LayoutParams(mTvTipText.getLayoutParams());
textlayoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT;
textlayoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
textlayoutParams.topMargin = (int) mRectScreen.bottom + 2 * offset;
textlayoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
mTvTipText.setLayoutParams(textlayoutParams);
//toast相对居中,减去框的高度/2, 再减去自身的高度/2
RelativeLayout.LayoutParams myToastParams = new RelativeLayout.LayoutParams(mLayoutToast.getLayoutParams());
myToastParams.width = RelativeLayout.LayoutParams.WRAP_CONTENT;
myToastParams.height = RelativeLayout.LayoutParams.WRAP_CONTENT;
myToastParams.topMargin = layoutParams.topMargin + layoutParams.height / 2 - (int) getResources().getDimension(R.dimen.checktrue_toast_text_padding_height);
myToastParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
mLayoutToast.setLayoutParams(myToastParams);
//完整对勾的位置
RelativeLayout.LayoutParams ivIconTick = new RelativeLayout.LayoutParams(mIvIconTickAll.getLayoutParams());
ivIconTick.width = RelativeLayout.LayoutParams.WRAP_CONTENT;
ivIconTick.height = RelativeLayout.LayoutParams.WRAP_CONTENT;
ivIconTick.topMargin = layoutParams.topMargin + layoutParams.height / 2 - (int) getResources().getDimension(R.dimen.checktrue_bg_tick_bg_height);
ivIconTick.addRule(RelativeLayout.CENTER_HORIZONTAL);
mIvIconTickAll.setLayoutParams(ivIconTick);
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
Camera camera = mICamera.openCamera(this);
if (camera != null) {
initIdcardGuide();
mHasSurface = true;
doPreview();
mICamera.setPreviewCallback(this);
lastFaileType = IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_UNKNOWN;
mBeginTime = System.currentTimeMillis();
isCanDetected = true;
mTextureView.post(new Runnable() {
@Override
public void run() {
initIdCardRect();
}
});
if (mDecoder == null) {
mDecoder = new DecodeThread();
}
if (mDecoder != null && !mDecoder.isAlive()) {
mDecoder.start();
}
startBreatheAlphaAnimation(mIbAnimalBreathView);
startBreatheAlphaAnimation(mTvTipText);
} else {
Toast.makeText(this, "打开摄像头失败", Toast.LENGTH_SHORT).show();
doFinish();
return;
}
}
private void initIdcardGuide() {
RelativeLayout.LayoutParams layout_params = mICamera.getLayoutParam(this);
if (mIsVertical) {
mIdcardGuide.setLayoutParams(layout_params);
} else {
layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL);
mIdcardGuideH.setLayoutParams(layout_params);
}
mTextureView.setLayoutParams(layout_params);
if (!mIsVertical) {
layout_params.height = RelativeLayout.LayoutParams.WRAP_CONTENT;
mTitleBarH.setLayoutParams(layout_params);
}
}
@Override
protected void onPause() {
super.onPause();
if (!isDetectFinished) {
doFinish();
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
mICamera.closeCamera();
mHasSurface = false;
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
private class DecodeThread extends Thread {
boolean mHasFinished = false;
public void setmHasSuccess(boolean mHasFinished) {
this.mHasFinished = mHasFinished;
}
@Override
public void run() {
try {
byte[] image = null;
while ((image = mFrameDataQueue.take()) != null) {
if (mHasFinished) {//两面全部检测通过
return;
}
if (!isCanDetected) {
continue;
}
mImageWidth = mICamera.cameraWidth;
mImageHeight = mICamera.cameraHeight;
mImgData = RotaterUtil.rotate(image, mImageWidth, mImageHeight,
mICamera.getCameraAngle(IDCardDetectActivity.this));
if (mIsVertical) {
mImageWidth = mICamera.cameraHeight;
mImageHeight = mICamera.cameraWidth;
}
mQualityResult = mIdCardQualityAssessment.getQuality(mImgData, mImageWidth,
mImageHeight, mSide, mRoi);
final boolean isSuccess = mQualityResult.isValid();
mDecoder.setmHasSuccess(isSuccess);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (isSuccess) {
handleSuccessResult();
} else {
IDCardQualityResult.IDCardResultType resultType = mQualityResult.idCardResultType;
//以下几种情况隐藏身份证人像面或者国徽面图标icon
if (resultType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NONE
|| resultType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NOTINBOUND
|| resultType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NOTCLEAR
|| resultType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_HAVEHIGHLIGHT
|| resultType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_HAVESHADOW
|| resultType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_CONVERT) {
dismissIcon();
} else {
showIcon();
}
showFaileToast(resultType);
}
}
});
}
} catch (
Throwable e) {
e.printStackTrace();
}
}
}
private Bitmap iDCardImg = null;
private Bitmap portraitImg = null;
private void handleSuccessResult() {
cancelMyToast();
setBlueLine();
mIbAnimalBreathView.setVisibility(View.GONE);
mIvIconTickAll.setVisibility(View.VISIBLE);
Intent intent = new Intent();
iDCardImg = mQualityResult.croppedImageOfIDCard();
intent.putExtra("idcardimg_bitmap", CommonUtils.bmp2byteArr(iDCardImg));
if (mQualityResult.attr.side == IDCardAttr.IDCardSide.IDCARD_SIDE_FRONT) {
portraitImg = mQualityResult.croppedImageOfPortrait();
intent.putExtra("portraitimg_bitmap", CommonUtils.bmp2byteArr(portraitImg));
}
setResult(RESULT_OK, intent);
doFinish();
}
private long toastTime = 0;
private IDCardQualityResult.IDCardResultType lastFaileType;
private long lastTime = 0;
private long firstTime = 0;
private boolean isFirstToast = true;
private boolean canShowAnimal = true;
private boolean isTheSameError = true;
private void showFaileToast(IDCardQualityResult.IDCardResultType faileType) {
try {
//检测5s后开始弹toast
if (System.currentTimeMillis() - mBeginTime <= 5000) {
return;
}
if (System.currentTimeMillis() - toastTime < 500) {
return;
}
//计算同一个错误出现的时间
if (faileType == lastFaileType) {
isTheSameError = true;
if (isFirstToast) {
firstTime = System.currentTimeMillis();
lastTime = firstTime;
isFirstToast = false;
}
lastTime += (System.currentTimeMillis() - lastTime);
} else {
isTheSameError = false;
firstTime = System.currentTimeMillis();
lastTime = firstTime;
//两个toast,间隔500毫秒
if (isMyToastShown()) {
lastFaileType = faileType;
cancelMyToast();
toastTime = System.currentTimeMillis();
return;
}
}
long continueTime = lastTime - firstTime;
//超过2秒后,弹出动画
if (continueTime > 2000) {
if (canShowAnimal) {//闪烁动画只出现一次
canShowAnimal = false;
startToastTipAnimal();
if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NEEDFRONT) {//需要人像面
startPeopleIconAlphaAnimation();
} else if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NEEDBACK) {
startChinaIconAlphaAnimation();
}
autoFocus();
}
} else {
canShowAnimal = true;
cancelToastTipAnimal();
cancelPeopleIconAlphaAnimation();
cancelChinaIconAlphaAnimation();
}
lastFaileType = faileType;
String toastStr = "";
if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NOTIDCARD) {//没有检测到身份证
toastStr = getResources().getString(R.string.remind_idcard_quality_failed_1);
} else if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NOTINBOUND) {//身份证不在引导框内
toastStr = getResources().getString(R.string.remind_idcard_quality_failed_2);
} else if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NOTCLEAR) {//身份证清晰度太低
toastStr = getResources().getString(R.string.remind_idcard_quality_failed_3);
} else if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_HAVEHIGHLIGHT) {//存在光斑
toastStr = getResources().getString(R.string.remind_idcard_quality_failed_4);
} else if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_HAVESHADOW) {//存在阴影
toastStr = getResources().getString(R.string.remind_idcard_quality_failed_5);
} else if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NEEDFRONT) {//需要检测身份人像面
toastStr = getResources().getString(R.string.remind_idcard_quality_failed_6);
} else if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NEEDBACK) {//需要检测身份证国徽面
toastStr = getResources().getString(R.string.remind_idcard_quality_failed_7);
} else if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_NEEDBACK) {//需要检测身份证国徽面
toastStr = getResources().getString(R.string.remind_idcard_quality_failed_7);
} else if (faileType == IDCardQualityResult.IDCardResultType.IDCARD_QUALITY_FAILED_CONVERT) {
toastStr = getResources().getString(R.string.remind_idcard_quality_failed_8);
}
if (!"".equals(toastStr)) {
final String finalToastStr = toastStr;
if (isTheSameError) {
showMyToast(finalToastStr, false);
} else {
showMyToast(finalToastStr, true);
}
toastTime = System.currentTimeMillis();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private void showIcon() {
mIbAnimalOneView.setVisibility(View.VISIBLE);
}
private void dismissIcon() {
mIbAnimalOneView.setVisibility(View.GONE);
}
public void autoFocus() {
try {
if (null != mICamera) {
mICamera.autoFocus();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
/**
* 展示toast前面的tip动画
*/
ObjectAnimator tipAnimation;
public void startToastTipAnimal() {
mToastTip.setVisibility(View.VISIBLE);
tipAnimation = ObjectAnimator.ofFloat(mToastTip, "alpha", 0.3f, 0.8f, 0.3f);
tipAnimation.setDuration(500);
tipAnimation.setRepeatCount(300);
tipAnimation.setInterpolator(new LinearInterpolator());
tipAnimation.setRepeatMode(ObjectAnimator.REVERSE);
tipAnimation.start();
}
/**
* 展示自定义toast
*
* @param msg
*/
private void showMyToast(final String msg, final boolean useCustomTime) {
if (!isMyToastShown()) {
mLayoutToast.setVisibility(View.VISIBLE);
mToastTitle.setText(msg);
if (useCustomTime) {
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
cancelMyToast();
}
});
}
}, 1000);
}
}
}
/**
* 展示自定义toast
*/
private void cancelMyToast() {
mLayoutToast.setVisibility(View.GONE);
cancelToastTipAnimal();
}
/**
* 自定义toast是否展示
*/
private boolean isMyToastShown() {
if (View.VISIBLE == mLayoutToast.getVisibility()) {
return true;
} else {
return false;
}
}
/**
* 隐藏toast前面的tip动画
*/
public void cancelToastTipAnimal() {
if (tipAnimation != null) {
tipAnimation.cancel();
tipAnimation.end();
tipAnimation.removeAllListeners();
tipAnimation.removeAllUpdateListeners();
tipAnimation = null;
}
mToastTip.setVisibility(View.GONE);
}
/**
* 取消人像面icon呼吸渐变动画
*/
private void cancelPeopleIconAlphaAnimation() {
if (null != mPeopleIconAlphaAnimation) {
mPeopleIconAlphaAnimation.cancel();
mPeopleIconAlphaAnimation.end();
mPeopleIconAlphaAnimation = null;
}
mIvPeopleIconlightView.setVisibility(View.GONE);
}
/**
* 取消人像面icon呼吸渐变动画
*/
private void cancelChinaIconAlphaAnimation() {
if (null != mChinaIconAlphaAnimation) {
mChinaIconAlphaAnimation.cancel();
mChinaIconAlphaAnimation.end();
mChinaIconAlphaAnimation = null;
}
mIvChinaIconLightView.setVisibility(View.GONE);
}
/**
* 国徽面icon呼吸渐变动画
*/
private void startChinaIconAlphaAnimation() {
mIvChinaIconLightView.setVisibility(View.VISIBLE);
mChinaIconAlphaAnimation = ObjectAnimator.ofFloat(mIvChinaIconLightView, "alpha", 0.2f, 0.8f, 0.2f);
mChinaIconAlphaAnimation.setDuration(500);
mChinaIconAlphaAnimation.setRepeatCount(100);
mChinaIconAlphaAnimation.setInterpolator(new LinearInterpolator());
mChinaIconAlphaAnimation.setRepeatMode(ObjectAnimator.REVERSE);
mChinaIconAlphaAnimation.start();
}
/**
* 人像面icon呼吸渐变动画
*/
private void startPeopleIconAlphaAnimation() {
mIvPeopleIconlightView.setVisibility(View.VISIBLE);
mPeopleIconAlphaAnimation = ObjectAnimator.ofFloat(mIvPeopleIconlightView, "alpha", 0.2f, 0.8f, 0.2f);
mPeopleIconAlphaAnimation.setDuration(500);
mPeopleIconAlphaAnimation.setRepeatCount(100);
mPeopleIconAlphaAnimation.setInterpolator(new LinearInterpolator());
mPeopleIconAlphaAnimation.setRepeatMode(ObjectAnimator.REVERSE);
mPeopleIconAlphaAnimation.start();
}
private void removeAllAnimation() {
if (mChinaIconAlphaAnimation != null) {
mChinaIconAlphaAnimation.cancel();
mChinaIconAlphaAnimation.end();
mChinaIconAlphaAnimation.removeAllListeners();
mChinaIconAlphaAnimation.removeAllUpdateListeners();
mChinaIconAlphaAnimation = null;
}
if (mPeopleIconAlphaAnimation != null) {
mPeopleIconAlphaAnimation.cancel();
mPeopleIconAlphaAnimation.end();
mPeopleIconAlphaAnimation.removeAllListeners();
mPeopleIconAlphaAnimation.removeAllUpdateListeners();
mPeopleIconAlphaAnimation = null;
}
if (mIDcardAlphaAnimation != null) {
mIDcardAlphaAnimation.cancel();
mIDcardAlphaAnimation.end();
mIDcardAlphaAnimation.removeAllListeners();
mIDcardAlphaAnimation.removeAllUpdateListeners();
mIDcardAlphaAnimation = null;
}
cancelChinaIconAlphaAnimation();
cancelPeopleIconAlphaAnimation();
}
/**
* 画蓝色的线
*/
private void setBlueLine() {
if (mIsVertical) {
mIdcardGuide.setDrawLine(false);
} else {
mIdcardGuideH.setDrawLine(false);
}
}
/**
* 常驻呼吸框渐变动画
*
* @param view
*/
private void startBreatheAlphaAnimation(final View view) {
view.setVisibility(View.VISIBLE);
if (view == mTvTipText) {
mIDcardAlphaAnimation = ObjectAnimator.ofFloat(view, "alpha", 1f, 0.3f, 1f);
} else {
mIDcardAlphaAnimation = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f, 1f);
}
mIDcardAlphaAnimation.setDuration(1500);
mIDcardAlphaAnimation.setRepeatCount(ObjectAnimator.INFINITE);
mIDcardAlphaAnimation.setRepeatMode(ObjectAnimator.REVERSE);
mIDcardAlphaAnimation.start();
}
// 用取余运算
public boolean isEven01(int num) {
if (num % 2 == 0) {
return true;
} else {
return false;
}
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.rl_megvii_idcard_cn_goback || v.getId() == R.id.rl_megvii_idcard_cn_goback_h) {
doFinish();
} else if (v.getId() == R.id.idcardscan_cn_layout_guide || v.getId() == R.id.idcardscan_cn_layout_guide_h
|| v.getId() == R.id.idcardscan_cn_layout_surface) {
if (mICamera != null) {
mICamera.autoFocus();
}
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
doFinish();
return true;
}
return super.onKeyDown(keyCode, event);
}
private void doFinish() {
try {
if (mDecoder != null) {
mDecoder.setmHasSuccess(true);
mDecoder.interrupt();
mDecoder.join();
mDecoder = null;
}
} catch (Throwable e) {
e.printStackTrace();
}
if (mFrameDataQueue != null) {
mFrameDataQueue.clear();
}
mIdCardQualityAssessment.release();
removeAllAnimation();
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
package com.megvii.demo.bo;
public class Constant {
public static final String ISVERTICAL="ISVERTICAL";
public static final String CARD_SIDE="CARDSIDE";
public static final String VERSION_ID="MegIDCardQuality 1.4.1A";
public final static String CACHEIMAGE = "idcard";
}
package com.megvii.demo.utils;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.view.View;
import com.megvii.demo.bo.Constant;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class CommonUtils {
/**
* 把dp根据当前屏幕密度转换成px
*
* @param context
* @param dpValue
* @return
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 保存bitmap至指定Picture文件夹
*/
public static String saveBitmap(Context mContext, Bitmap bitmaptosave, String fileName) {
if (bitmaptosave == null)
return null;
File mediaStorageDir = mContext
.getExternalFilesDir(Constant.CACHEIMAGE);
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
return null;
}
}
// String bitmapFileName = System.currentTimeMillis() + ".jpg";
// String bitmapFileName = System.currentTimeMillis() + "";
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mediaStorageDir + "/" + fileName);
boolean successful = bitmaptosave.compress(
Bitmap.CompressFormat.JPEG, 100, fos);
if (successful)
return mediaStorageDir.getAbsolutePath() + "/" + fileName;
else
return null;
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void toggleHideyBar(Activity activity) {
int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
int newUiOptions = uiOptions;
if (Build.VERSION.SDK_INT >= 14) {
newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
if (Build.VERSION.SDK_INT >= 16) {
newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
}
if (Build.VERSION.SDK_INT >= 19) {
newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
activity.getWindow().getDecorView().setSystemUiVisibility(newUiOptions);
}
public static byte[] bmp2byteArr(Bitmap bmp) {
if (bmp == null || bmp.isRecycled())
return null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, 90, byteArrayOutputStream);
if (byteArrayOutputStream != null) {
try {
byteArrayOutputStream.close();
} catch (IOException e) {
}
}
return byteArrayOutputStream.toByteArray();
}
public static String saveBytw(Context context, byte[] b, String fileName) {
BufferedOutputStream stream = null;
File file = null;
try {
file = context.getExternalFilesDir("megvii");
FileOutputStream fstream = new FileOutputStream(file + "/" + fileName + "_" + System.currentTimeMillis());
stream = new BufferedOutputStream(fstream);
stream.write(b);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return file.getAbsolutePath();
}
}
package com.megvii.demo.utils;
import android.content.Context;
import android.util.Base64;
import com.megvii.demo.bo.Constant;
import java.util.UUID;
public class Configuration {
public static String getUUID(Context context) {
String uuid = SpFileUtil.getString(context, SpFileUtil.FILE_NAME, SpFileUtil.KEY_UUID, "");
if ("".equals(uuid)) {
uuid = UUID.randomUUID().toString();
uuid = Base64.encodeToString(uuid.getBytes(), Base64.DEFAULT);
SpFileUtil.saveString(context, SpFileUtil.FILE_NAME, SpFileUtil.KEY_UUID, uuid);
}
return uuid;
}
public static void setIsVertical(Context context, boolean ISVERTICAL) { //屏幕方向 0竖屏 1 横屏
SpFileUtil.saveBoolean(context, SpFileUtil.FILE_NAME, Constant.ISVERTICAL, ISVERTICAL);
}
public static boolean getIsVertical(Context context) {
return SpFileUtil.getBoolean(context, SpFileUtil.FILE_NAME, Constant.ISVERTICAL, true);
}
public static void setCardType(Context context, int cardType) { //0:双面,1:人像面,2:国徽面(默认人像面)
SpFileUtil.saveInt(context, SpFileUtil.FILE_NAME, Constant.CARD_SIDE, cardType);
}
public static int getCardType(Context context) {
return SpFileUtil.getInt(context, SpFileUtil.FILE_NAME, Constant.CARD_SIDE, 1);
}
}
package com.megvii.demo.utils;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.util.Log;
import android.view.Surface;
import android.widget.RelativeLayout;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ICamera {
public Camera mCamera;
public int cameraWidth;
public int cameraHeight;
private int cameraId = 0;// 后置摄像头
private boolean mIsVertical = false;
private int screenWidth;
private int screenHeight;
public ICamera(boolean isVertical) {
this.mIsVertical = isVertical;
}
/**
* 打开相机
*/
public Camera openCamera(Activity activity) {
try {
screenWidth = activity.getWindowManager().getDefaultDisplay()
.getWidth();
screenHeight = activity.getWindowManager().getDefaultDisplay()
.getHeight();
mCamera = Camera.open(cameraId);
CameraInfo cameraInfo = new CameraInfo();
Camera.getCameraInfo(cameraId, cameraInfo);
Parameters params = mCamera.getParameters();
Camera.Size bestPreviewSize = getNearestRatioSize(
mCamera.getParameters(), screenWidth, screenHeight);
cameraWidth = bestPreviewSize.width;
cameraHeight = bestPreviewSize.height;
params.setPreviewSize(cameraWidth, cameraHeight);
List<String> focusModes = params.getSupportedFocusModes();
if (focusModes
.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
params.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
int rotation = getCameraAngle(activity);
if (!mIsVertical && cameraId == 0){
mCamera.setDisplayOrientation(0);
}else{
mCamera.setDisplayOrientation(getCameraAngle(activity));
}
mCamera.setParameters(params);
return mCamera;
} catch (Exception e) {
return null;
}
}
public void autoFocus() {
try {
if (mCamera != null) {
Parameters parameters = mCamera.getParameters();
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes.contains(Parameters.FOCUS_MODE_AUTO)) {
parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
mCamera.cancelAutoFocus();
parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
mCamera.setParameters(parameters);
mCamera.autoFocus(null);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 通过屏幕参数、相机预览尺寸计算布局参数
public RelativeLayout.LayoutParams getLayoutParam(Activity activity) {
float scale = cameraWidth * 1.0f / cameraHeight;
int layout_width = screenWidth;
int layout_height = (int) (layout_width * 1.0f * scale);
Log.w("layoutParam:","screenWidth = "+screenWidth);
Log.w("layoutParam:","layout_width = "+layout_width);
Log.w("layoutParam:","layout_height = "+layout_height);
if(!mIsVertical){
layout_height = screenHeight;
layout_width = (int) (layout_height * 1.0f * scale);
Log.w("layoutParam:","layout_height = "+layout_height);
Log.w("layoutParam:","layout_width = "+layout_width);
}
RelativeLayout.LayoutParams layout_params = new RelativeLayout.LayoutParams(
layout_width, layout_height);
return layout_params;
}
/**
* 预览回调
*/
public void setPreviewCallback(Camera.PreviewCallback mActivity) {
// public void actionDetect(Camera.PreviewCallback mActivity) {
try {
if (mCamera != null) {
mCamera.setPreviewCallback(mActivity);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void startPreview(SurfaceTexture surfaceTexture) {
try {
if (mCamera != null) {
try {
mCamera.setPreviewTexture(surfaceTexture);
mCamera.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void closeCamera() {
try {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 通过传入的宽高算出最接近于宽高值的相机大小
*/
private Camera.Size calBestPreviewSize(Parameters camPara,
final int width, final int height) {
List<Camera.Size> allSupportedSize = camPara.getSupportedPreviewSizes();
ArrayList<Camera.Size> widthLargerSize = new ArrayList<Camera.Size>();
for (Camera.Size tmpSize : allSupportedSize) {
if (tmpSize.width > tmpSize.height) {
widthLargerSize.add(tmpSize);
}
}
Collections.sort(widthLargerSize, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size lhs, Camera.Size rhs) {
int off_one = Math.abs(lhs.width * lhs.height - width * height);
int off_two = Math.abs(rhs.width * rhs.height - width * height);
return off_one - off_two;
}
});
return widthLargerSize.get(0);
}
public Camera.Size getNearestRatioSize(Parameters para,
final int screenWidth, final int screenHeight) {
List<Camera.Size> supportedSize = para.getSupportedPreviewSizes();
if (mIsVertical) {
for (Camera.Size tmp : supportedSize) {
if (tmp.width == 640 && tmp.height == 480) {
return tmp;
}
}
}else{
for (Camera.Size tmp : supportedSize) {
if (tmp.width == 1280 && tmp.height == 720) {
return tmp;
}
}
}
Collections.sort(supportedSize, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size lhs, Camera.Size rhs) {
int diff1 = (((int) ((1000 * (Math.abs(lhs.width
/ (float) lhs.height - screenWidth
/ (float) screenHeight))))) << 16)
- lhs.width;
int diff2 = (((int) (1000 * (Math.abs(rhs.width
/ (float) rhs.height - screenWidth
/ (float) screenHeight)))) << 16)
- rhs.width;
return diff1 - diff2;
}
});
return supportedSize.get(0);
}
/**
* 打开前置或后置摄像头
*/
public Camera getCameraSafely(int cameraId) {
Camera camera = null;
try {
camera = Camera.open(cameraId);
} catch (Exception e) {
camera = null;
}
return camera;
}
public RelativeLayout.LayoutParams getParams(Camera camera) {
Parameters camPara = camera.getParameters();
// 注意Screen是否初始化
Camera.Size bestPreviewSize = calBestPreviewSize(camPara, screenWidth,
screenHeight);
cameraWidth = bestPreviewSize.width;
cameraHeight = bestPreviewSize.height;
camPara.setPreviewSize(cameraWidth, cameraHeight);
camera.setParameters(camPara);
float scale = bestPreviewSize.width / bestPreviewSize.height;
RelativeLayout.LayoutParams layoutPara = new RelativeLayout.LayoutParams(
(int) (bestPreviewSize.width),
(int) (bestPreviewSize.width / scale));
layoutPara.addRule(RelativeLayout.CENTER_HORIZONTAL);// 设置照相机水平居中
return layoutPara;
}
public Bitmap getBitMap(byte[] data, Camera camera, boolean mIsFrontalCamera) {
int width = camera.getParameters().getPreviewSize().width;
int height = camera.getParameters().getPreviewSize().height;
YuvImage yuvImage = new YuvImage(data, camera.getParameters()
.getPreviewFormat(), width, height, null);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 80,
byteArrayOutputStream);
byte[] jpegData = byteArrayOutputStream.toByteArray();
// 获取照相后的bitmap
Bitmap tmpBitmap = BitmapFactory.decodeByteArray(jpegData, 0,
jpegData.length);
Matrix matrix = new Matrix();
matrix.reset();
if (mIsFrontalCamera) {
matrix.setRotate(-90);
} else {
matrix.setRotate(90);
}
tmpBitmap = Bitmap.createBitmap(tmpBitmap, 0, 0, tmpBitmap.getWidth(),
tmpBitmap.getHeight(), matrix, true);
tmpBitmap = tmpBitmap.copy(Bitmap.Config.ARGB_8888, true);
int hight = tmpBitmap.getHeight() > tmpBitmap.getWidth() ? tmpBitmap
.getHeight() : tmpBitmap.getWidth();
float scale = hight / 800.0f;
if (scale > 1) {
tmpBitmap = Bitmap.createScaledBitmap(tmpBitmap,
(int) (tmpBitmap.getWidth() / scale),
(int) (tmpBitmap.getHeight() / scale), false);
}
return tmpBitmap;
}
/**
* 获取照相机旋转角度
*/
public int getCameraAngle(Activity activity) {
int rotateAngle = 90;
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
rotateAngle = (info.orientation + degrees) % 360;
rotateAngle = (360 - rotateAngle) % 360; // compensate the mirror
} else { // back-facing
rotateAngle = (info.orientation - degrees + 360) % 360;
}
return rotateAngle;
}
}
\ No newline at end of file
package com.megvii.demo.utils;
import android.content.Context;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class ModelUtil {
public static byte[] readModel(Context context, int rawId) {
InputStream inputStream = null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count = -1;
try {
inputStream = context.getResources().openRawResource(rawId);
while ((count = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, count);
}
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return byteArrayOutputStream.toByteArray();
}
}
package com.megvii.demo.utils;
public class RotaterUtil {
public static byte[] rotate(byte[] data, int imageWidth, int imageHeight,
int rotate) {
switch (rotate) {
case 0:
return data;
case 90:
return rotateYUV420Degree90(data, imageWidth, imageHeight);
case 180:
return rotateYUV420Degree180(data, imageWidth, imageHeight);
case 270:
return rotateYUV420Degree270(data, imageWidth, imageHeight);
}
return data;
}
public static byte[] rotateYUV420Degree90(byte[] data, int imageWidth,
int imageHeight) {
byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
// Rotate the Y luma
int i = 0;
for (int x = 0; x < imageWidth; x++) {
for (int y = imageHeight - 1; y >= 0; y--) {
yuv[i] = data[y * imageWidth + x];
i++;
}
}
// Rotate the U and V color components
i = imageWidth * imageHeight * 3 / 2 - 1;
for (int x = imageWidth - 1; x > 0; x = x - 2) {
for (int y = 0; y < imageHeight / 2; y++) {
yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
i--;
yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth)
+ (x - 1)];
i--;
}
}
return yuv;
}
public static byte[] rotateYUV420Degree180(byte[] data, int imageWidth,
int imageHeight) {
byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
int i = 0;
int count = 0;
for (i = imageWidth * imageHeight - 1; i >= 0; i--) {
yuv[count] = data[i];
count++;
}
i = imageWidth * imageHeight * 3 / 2 - 1;
for (i = imageWidth * imageHeight * 3 / 2 - 1; i >= imageWidth
* imageHeight; i -= 2) {
yuv[count++] = data[i - 1];
yuv[count++] = data[i];
}
return yuv;
}
public static byte[] rotateYUV420Degree270(byte[] data, int imageWidth,
int imageHeight) {
byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
// Rotate the Y luma
int index = 0;
int i = 0;
for (int x = imageWidth - 1; x >= 0; x--) {
index = 0;
for (int y = 0; y < imageHeight; y++) {
yuv[i] = data[index + x];
i++;
index += imageWidth;
}
}
// Rotate the U and V color components
i = imageWidth * imageHeight;
int count = imageWidth * imageHeight;
for (int x = imageWidth - 1; x > 0; x = x - 2) {
index = count;
for (int y = 0; y < imageHeight / 2; y++) {
yuv[i] = data[index + (x - 1)];
i++;
yuv[i] = data[index + x];
i++;
index += imageWidth;
}
}
return yuv;
}
}
package com.megvii.demo.utils;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.Map;
public class SpFileUtil {
/**
* 保存在手机里的文件名
*/
public static final String FILE_NAME = "meg_idcard_quality";
public static final String KEY_UUID = "key_uuid";
/**
* @WARNING Be careful, this maybe overwrite a exist value.<br>
*/
public static void removeKey(Context context, String filename, String key) {
if (context != null) {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.remove(key);
editor.apply();
}
}
/**
* @WARNNING Be careful, this maybe overwrite a exist value.<br>
*/
public static void saveString(Context context, String filename, String key, String value) {
if (context != null) {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(key, value);
editor.apply();
}
}
/**
* get String from shared Preferences.<br>
*
* @PROMPT if the img_demo_target value is not exist, it will return the defaultValue.
*/
public static String getString(Context context, String filename, String key, String defaultValue) {
try {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
return preferences.getString(key, defaultValue);
} catch (Exception e) {
e.printStackTrace();
}
return defaultValue;
}
/**
* @WARNNING Be careful, this maybe overwrite a exist value.<br>
*/
public static void savelong(Context context, String filename, String key, long value) {
if (context != null) {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putLong(key, value);
editor.apply();
}
}
/**
* get long value from shared Preferences.<br>
*
* @PROMPT if the img_demo_target value is not exist, it will return the defaultValue.
*/
public static long getlong(Context context, String filename, String key, long defaultValue) {
try {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
return preferences.getLong(key, defaultValue);
} catch (Exception e) {
e.printStackTrace();
}
return defaultValue;
}
/**
* @WARNNING Be careful, this maybe overwrite a exist value.<br>
*/
public static void saveInt(Context context, String filename, String key, int value) {
if (context != null) {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putInt(key, value);
editor.apply();
}
}
/**
* get int value from shared Preferences.<br>
*
* @PROMPT if the img_demo_target value is not exist, it will return the defaultValue.
*/
public static int getInt(Context context, String filename, String key, int defaultValue) {
try {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
return preferences.getInt(key, defaultValue);
} catch (Exception e) {
e.printStackTrace();
}
return defaultValue;
}
/**
* @WARNNING Be careful, this maybe overwrite a exist value.<br>
*/
public static void saveFloat(Context context, String filename, String key, float value) {
if (context != null) {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putFloat(key, value);
editor.apply();
}
}
/**
* get float value from shared Preferences.<br>
*
* @PROMPT if the img_demo_target value is not exist, it will return the defaultValue.
*/
public static float getFloat(Context context, String filename, String key, float defaultValue) {
try {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
return preferences.getFloat(key, defaultValue);
} catch (Exception e) {
e.printStackTrace();
}
return defaultValue;
}
/**
* @WARNNING Be careful, this maybe overwrite a exist value.<br>
*/
public static void saveBoolean(Context context, String filename, String key, boolean value) {
if (context != null) {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(key, value);
editor.apply();
}
}
/**
* get boolean value from shared Preferences.<br>
*
* @PROMPT if the img_demo_target value is not exist, it will return the defaultValue.
*/
public static boolean getBoolean(Context context, String filename, String key, boolean defaultValue) {
try {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
return preferences.getBoolean(key, defaultValue);
} catch (Exception e) {
e.printStackTrace();
}
return defaultValue;
}
/**
* get key-value map of particular filename from shared Preferences.<br>
*/
public static Map<String, ?> getFile(Context context, String filename) {
try {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
return preferences.getAll();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void clearFile(Context context, String filename) {
try {
SharedPreferences preferences = context.getSharedPreferences(filename, Context.MODE_PRIVATE);
preferences.edit().clear().apply();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 保存ContentValues 的数据
*
* @param mContext
* @param contentValues
*/
public static void saveData(Context mContext, ContentValues contentValues) {
try {
int size = (contentValues != null && contentValues.size() > 0)
? contentValues.size() : 0;
if (size > 0) {
for (String colName : contentValues.keySet()) {
SpFileUtil.saveString(mContext, SpFileUtil.FILE_NAME, colName, contentValues.get(colName).toString());
}
}
} catch (Throwable e) {
}
}
}
package com.megvii.demo.utils;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
public class StatusBarCompat {
private static final int INVALID_VAL = -1;
private static final int COLOR_DEFAULT = Color.parseColor("#20000000");
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static void compat(Activity activity, int statusColor) {
//当前手机版本为5.0及以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (statusColor != INVALID_VAL) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);//设置高亮反转
activity.getWindow().setStatusBarColor(statusColor);
}
return;
}
//当前手机版本为4.4
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
int color = COLOR_DEFAULT;
ViewGroup contentview = (ViewGroup) activity.findViewById(android.R.id.content);
if (statusColor != INVALID_VAL) {
color = statusColor;
}
View statusBarView = new View(activity);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
getStatusBarHeight(activity)
);
statusBarView.setBackgroundColor(color);
contentview.addView(statusBarView, lp);
}
}
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}
\ No newline at end of file
package com.megvii.demo.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import com.megvii.idcard.quality.R;
import com.megvii.idcardquality.bean.IDCardAttr;
public class IDCardGuide extends View {
private Bitmap mCircleBmp;
private Canvas mCanvas;
private float IDCARD_RATIO = 856.0f / 540.0f;
private int mViewWidth = 0;
private int mViewHeight = 0;
private float reduceRatio = 1.0f;
private Rect srcRect;
private Rect bitmapRect;
private RectF roundRec;
private RectF disRectF;
private Paint mRectPaint;
private Paint mCirclePaint;
private Paint duffXmodePaint;
private PorterDuffXfermode duffXmode;
private int rectType = 1;//1小框80% 2 大框88%
private float roundRectLeft1;
private float roundRectTop1;
private float roundRectRight1;
private float roundRectBottom1;
private float roundRectLeft;
private float roundRectTop;
private float roundRectRight;
private float roundRectBottom;
private float roundRectLeft2;
private float roundRectTop2;
private float roundRectRight2;
private float roundRectBottom2;
private boolean isDrawImage = true;//是否画框
public static final float BIG_RECT_PERCENT = 0.85f;//大框的比例
public static final float SMALL_RECT_PERCENT = 0.65f;//小框的比例
public IDCardGuide(Context context) {
this(context, null);
}
public IDCardGuide(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public IDCardGuide(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void computeArgs() {
mViewWidth = getWidth();
mViewHeight = getHeight();
}
private void init(Context context) {
srcRect = new Rect();
disRectF = new RectF();
roundRec = new RectF();
bitmapRect = new Rect();
mRectPaint = new Paint();
mRectPaint.setAntiAlias(true);
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
duffXmodePaint = new Paint();
duffXmodePaint.setAntiAlias(true);
}
private Bitmap bitmap;
public void setCardSide(IDCardAttr.IDCardSide cardSide) {
int bitmapId = R.mipmap.bg_sfz_empty_icon;
bitmap = BitmapFactory.decodeResource(getContext().getResources(), bitmapId);
isDrawImage = true;
isDrawLine = true;
rectType = 1;
roundRectColor = Color.parseColor("#FFFFFF");
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
float roundRectWidth = width * BIG_RECT_PERCENT;
float roundRectHeight = roundRectWidth / IDCARD_RATIO;
roundRectLeft1 = (width - roundRectWidth) / 2;
roundRectRight1 = roundRectLeft1 + roundRectWidth;
float previewHeight = width * (4 / 3.0f);
float roundCenterY = previewHeight * (2 / 5.0f) + getResources().getDimension(R.dimen.checktrue_title_bar_height);
roundRectTop1 = roundCenterY - roundRectHeight / 2;
roundRectBottom1 = roundCenterY + roundRectHeight / 2;
roundRectWidth = width * SMALL_RECT_PERCENT;
roundRectHeight = roundRectWidth / IDCARD_RATIO;
roundRectLeft2 = (width - roundRectWidth) / 2;
roundRectRight2 = roundRectLeft2 + roundRectWidth;
roundRectTop2 = roundCenterY - roundRectHeight / 2;
roundRectBottom2 = roundCenterY + roundRectHeight / 2;
roundRectLeft = rectType == 1 ? roundRectLeft1 : roundRectLeft2;
roundRectRight = rectType == 1 ? roundRectRight1 : roundRectRight2;
roundRectTop = rectType == 1 ? roundRectTop1 : roundRectTop2;
roundRectBottom = rectType == 1 ? roundRectBottom1 : roundRectBottom2;
}
@Override
protected void onDraw(Canvas canvas) {
computeArgs();
drawRectAndEmpCicle();
disRectF.set(0f, 0f, getWidth(), getHeight());
canvas.drawBitmap(mCircleBmp, srcRect, disRectF, mRectPaint);
drawRoundRect(canvas);
}
/**
* 画一个挖出圆角空间的矩形(因为效率原因在绘画时先进行缩放)
*
* @return
*/
private void drawRectAndEmpCicle() {
mRectPaint.setColor(Color.parseColor("#99000000"));
// mRectPaint.setColor(Color.argb(125, 0, 0, 0));
//阴影层图像
if (mCircleBmp == null) {
mCircleBmp = Bitmap.createBitmap((int) (mViewWidth / reduceRatio), (int) (mViewHeight / reduceRatio), Bitmap.Config.ARGB_8888);
}
if (mCanvas == null) {
mCanvas = new Canvas(mCircleBmp);
}
srcRect.set(0, 0, (int) (mViewWidth / reduceRatio), (int) (mViewHeight / reduceRatio));
mCanvas.drawRect(srcRect, mRectPaint);
if (isDrawImage) {
if (duffXmode == null) {
duffXmode = new PorterDuffXfermode(PorterDuff.Mode.XOR);
}
duffXmodePaint.setXfermode(duffXmode);
roundRec.set(roundRectLeft / reduceRatio, roundRectTop / reduceRatio, roundRectRight / reduceRatio, roundRectBottom / reduceRatio);
mCanvas.drawRoundRect(roundRec, 20, 20, duffXmodePaint);
duffXmodePaint.setXfermode(null);
}
}
private boolean isDrawLine = false;
public void setDrawLine(boolean flag) {
isDrawLine = flag;
if (!flag) {
roundRectColor = Color.parseColor("#67DAFF");
}
invalidate();
}
private int roundRectColor = Color.parseColor("#FFFFFF");
private void drawRoundRect(Canvas canvas) {
if (isDrawImage) {
mCirclePaint.setColor(roundRectColor);
if (!isDrawLine) {//蓝色线
mCirclePaint.setStrokeWidth(getResources().getDimension(R.dimen.dimen_3));
} else {//白色线
mCirclePaint.setStrokeWidth(getResources().getDimension(R.dimen.dimen_1));
}
mCirclePaint.setStyle(Paint.Style.STROKE);
roundRec.set(roundRectLeft, roundRectTop, roundRectRight, roundRectBottom);
canvas.drawRoundRect(roundRec, 20, 20, mCirclePaint);
}
}
public RectF getPosition(int rectType) {
RectF rectF = new RectF();
if (rectType == 1) {
rectF.left = roundRectLeft1 / (float) getWidth();
rectF.top = roundRectTop1 / (float) getHeight();
rectF.right = roundRectRight1 / (float) getWidth();
rectF.bottom = roundRectBottom1 / (float) getHeight();
} else if (rectType == 2) {
rectF.left = roundRectLeft2 / (float) getWidth();
rectF.top = roundRectTop2 / (float) getHeight();
rectF.right = roundRectRight2 / (float) getWidth();
rectF.bottom = roundRectBottom2 / (float) getHeight();
}
return rectF;
}
public RectF getScreenPosition(int rectType) {
RectF rectF = new RectF();
if (rectType == 1) {
rectF.left = roundRectLeft1;
rectF.top = roundRectTop1;
rectF.right = roundRectRight1;
rectF.bottom = roundRectBottom1;
} else if (rectType == 2) {
rectF.left = roundRectLeft2;
rectF.top = roundRectTop2;
rectF.right = roundRectRight2;
rectF.bottom = roundRectBottom2;
}
return rectF;
}
}
package com.megvii.demo.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import com.megvii.idcard.quality.R;
import com.megvii.idcardquality.bean.IDCardAttr;
public class IDCardGuideH extends View {
private Bitmap mCircleBmp;
private Canvas mCanvas;
private float IDCARD_RATIO = 856.0f / 540.0f;
private int mViewWidth = 0;
private int mViewHeight = 0;
private float reduceRatio = 1.0f;
private Rect srcRect;
private Rect bitmapRect;
private RectF roundRec;
private RectF disRectF;
private Paint mRectPaint;
private Paint mCirclePaint;
private Paint duffXmodePaint;
private PorterDuffXfermode duffXmode;
private int rectType = 1;//1小框80% 2 大框88%
private float roundRectLeft1;
private float roundRectTop1;
private float roundRectRight1;
private float roundRectBottom1;
private float roundRectLeft;
private float roundRectTop;
private float roundRectRight;
private float roundRectBottom;
private float roundRectLeft2;
private float roundRectTop2;
private float roundRectRight2;
private float roundRectBottom2;
private Bitmap lineBitmap;
private boolean isDrawImage = true;//是否画框
public static final float BIG_RECT_PERCENT = 0.95f;//大框的比例
public static final float SMALL_RECT_PERCENT = 0.65f;//小框的比例
public IDCardGuideH(Context context) {
this(context, null);
}
public IDCardGuideH(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public IDCardGuideH(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void computeArgs() {
mViewWidth = getWidth();
mViewHeight = getHeight();
}
private void init(Context context) {
srcRect = new Rect();
disRectF = new RectF();
roundRec = new RectF();
bitmapRect = new Rect();
mRectPaint = new Paint();
mRectPaint.setAntiAlias(true);
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
duffXmodePaint = new Paint();
duffXmodePaint.setAntiAlias(true);
}
private Bitmap bitmap;
public void setCardSide(IDCardAttr.IDCardSide cardSide) {
int bitmapId = R.mipmap.bg_sfz_empty_icon;
bitmap = BitmapFactory.decodeResource(getContext().getResources(), bitmapId);
isDrawImage = true;
rectType = 1;
roundRectColor = Color.parseColor("#FFFFFF");
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
float roundRectHeight = height * 0.52f;
// float roundRectHeight = width * (4 / 3.0f);
// float roundRectWidth = roundRectHeight * (4 / 3.0f);
float roundRectWidth = roundRectHeight * IDCARD_RATIO;
roundRectLeft1 = (width - roundRectWidth) / 2;
roundRectRight1 = roundRectLeft1 + roundRectWidth;
float roundCenterY = height / 2;
roundRectTop1 = roundCenterY - roundRectHeight / 2;
roundRectBottom1 = roundCenterY + roundRectHeight / 2;
roundRectHeight = height * 0.67f;
roundRectWidth = roundRectHeight * (3 / 4.0f);
// roundRectWidth = roundRectHeight * IDCARD_RATIO;
roundRectLeft2 = (width - roundRectWidth) / 2;
roundRectRight2 = roundRectLeft2 + roundRectWidth;
roundRectTop2 = roundCenterY - roundRectHeight / 2;
roundRectBottom2 = roundCenterY + roundRectHeight / 2;
roundRectLeft = rectType == 1 ? roundRectLeft1 : roundRectLeft2;
roundRectRight = rectType == 1 ? roundRectRight1 : roundRectRight2;
roundRectTop = rectType == 1 ? roundRectTop1 : roundRectTop2;
roundRectBottom = rectType == 1 ? roundRectBottom1 : roundRectBottom2;
// int width = MeasureSpec.getSize(widthMeasureSpec);
// int height = MeasureSpec.getSize(heightMeasureSpec);
//
// float roundRectWidth = width * BIG_RECT_PERCENT;
// float roundRectHeight = roundRectWidth / IDCARD_RATIO;
//
// roundRectLeft1 = (width - roundRectWidth) / 2;
// roundRectRight1 = roundRectLeft1 + roundRectWidth;
//
// float previewHeight = width * (4 / 3.0f);
// float roundCenterY = previewHeight * (2 / 5.0f) + getResources().getDimension(R.dimen.checktrue_title_bar_height);
//
// roundRectTop1 = roundCenterY - roundRectHeight / 2;
// roundRectBottom1 = roundCenterY + roundRectHeight / 2;
//
// roundRectWidth = width * SMALL_RECT_PERCENT;
// roundRectHeight = roundRectWidth / IDCARD_RATIO;
// roundRectLeft2 = (width - roundRectWidth) / 2;
// roundRectRight2 = roundRectLeft2 + roundRectWidth;
// roundRectTop2 = roundCenterY - roundRectHeight / 2;
// roundRectBottom2 = roundCenterY + roundRectHeight / 2;
//
// roundRectLeft = rectType == 1 ? roundRectLeft1 : roundRectLeft2;
// roundRectRight = rectType == 1 ? roundRectRight1 : roundRectRight2;
// roundRectTop = rectType == 1 ? roundRectTop1 : roundRectTop2;
// roundRectBottom = rectType == 1 ? roundRectBottom1 : roundRectBottom2;
}
@Override
protected void onDraw(Canvas canvas) {
computeArgs();
drawRectAndEmpCicle();
//将bitmap拷贝到canvas上并恢复原来大小
disRectF.set(0f, 0f, getWidth(), getHeight());
canvas.drawBitmap(mCircleBmp, srcRect, disRectF, mRectPaint);
drawRoundRect(canvas);
}
/**
* 画一个挖出圆角空间的矩形(因为效率原因在绘画时先进行缩放)
*
* @return
*/
private void drawRectAndEmpCicle() {
mRectPaint.setColor(Color.argb(125, 0, 0, 0));
//阴影层图像
if (mCircleBmp == null) {
mCircleBmp = Bitmap.createBitmap((int) (mViewWidth / reduceRatio), (int) (mViewHeight / reduceRatio), Bitmap.Config.ARGB_8888);
}
if (mCanvas == null) {
mCanvas = new Canvas(mCircleBmp);
}
srcRect.set(0, 0, (int) (mViewWidth / reduceRatio), (int) (mViewHeight / reduceRatio));
mCanvas.drawRect(srcRect, mRectPaint);
if (isDrawImage) {
if (duffXmode == null) {
duffXmode = new PorterDuffXfermode(PorterDuff.Mode.XOR);
}
duffXmodePaint.setXfermode(duffXmode);
roundRec.set(roundRectLeft / reduceRatio, roundRectTop / reduceRatio, roundRectRight / reduceRatio, roundRectBottom / reduceRatio);
mCanvas.drawRoundRect(roundRec, 20, 20, duffXmodePaint);
duffXmodePaint.setXfermode(null);
}
}
public void setDrawLine(boolean flag) {
if (!flag) {
roundRectColor = Color.parseColor("#3DDCFF");
}
invalidate();
}
private int roundRectColor = Color.parseColor("#FFFFFF");
private void drawRoundRect(Canvas canvas) {
if (isDrawImage) {
mCirclePaint.setColor(roundRectColor);
mCirclePaint.setStrokeWidth(getResources().getDimension(R.dimen.dimen_3));
mCirclePaint.setStyle(Paint.Style.STROKE);
roundRec.set(roundRectLeft, roundRectTop, roundRectRight, roundRectBottom);
canvas.drawRoundRect(roundRec, 20, 20, mCirclePaint);
bitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
canvas.drawBitmap(bitmap, bitmapRect, roundRec, null);
}
}
public RectF getPosition(int rectType) {
RectF rectF = new RectF();
if (rectType == 1) {
rectF.left = roundRectLeft1 / (float) getWidth();
rectF.top = roundRectTop1 / (float) getHeight();
rectF.right = roundRectRight1 / (float) getWidth();
rectF.bottom = roundRectBottom1 / (float) getHeight();
} else if (rectType == 2) {
rectF.left = roundRectLeft2 / (float) getWidth();
rectF.top = roundRectTop2 / (float) getHeight();
rectF.right = roundRectRight2 / (float) getWidth();
rectF.bottom = roundRectBottom2 / (float) getHeight();
}
return rectF;
}
public RectF getScreenPosition(int rectType) {
RectF rectF = new RectF();
if (rectType == 1) {
rectF.left = roundRectLeft1;
rectF.top = roundRectTop1;
rectF.right = roundRectRight1;
rectF.bottom = roundRectBottom1;
} else if (rectType == 2) {
rectF.left = roundRectLeft2;
rectF.top = roundRectTop2;
rectF.right = roundRectRight2;
rectF.bottom = roundRectBottom2;
}
return rectF;
}
}
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- <scale
android:duration="500"
android:fillAfter="true"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.8"
android:toYScale="0.8" /> -->
<translate
android:duration="300"
android:fromXDelta="100%p"
android:startOffset="100"
android:toXDelta="0" />
<!-- <scale
android:duration="500"
android:fillAfter="true"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="800"
android:toXScale="1.25"
android:toYScale="1.25" /> -->
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- <scale
android:duration="500"
android:fillAfter="true"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.8"
android:toYScale="0.8" /> -->
<translate
android:duration="300"
android:fromXDelta="-100%p"
android:startOffset="100"
android:toXDelta="0" />
<!-- <scale
android:duration="500"
android:fillAfter="true"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="800"
android:toXScale="1.25"
android:toYScale="1.25" /> -->
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<!--<scale-->
<!--android:duration="100"-->
<!--android:fillAfter="true"-->
<!--android:fromXScale="1.0"-->
<!--android:fromYScale="1.0"-->
<!--android:pivotX="50%"-->
<!--android:pivotY="50%"-->
<!--android:toXScale="0.95"-->
<!--android:toYScale="0.95" /> -->
<translate
android:duration="300"
android:fromXDelta="0"
android:startOffset="100"
android:toXDelta="-100%p" />
<!--<scale-->
<!--android:duration="100"-->
<!--android:fillAfter="true"-->
<!--android:fromXScale="1.0"-->
<!--android:fromYScale="1.0"-->
<!--android:pivotX="50%"-->
<!--android:pivotY="50%"-->
<!--android:startOffset="600"-->
<!--android:toXScale="1.05"-->
<!--android:toYScale="1.05" /> -->
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<!--<scale-->
<!--android:duration="100"-->
<!--android:fillAfter="true"-->
<!--android:fromXScale="1.0"-->
<!--android:fromYScale="1.0"-->
<!--android:pivotX="50%"-->
<!--android:pivotY="50%"-->
<!--android:toXScale="0.95"-->
<!--android:toYScale="0.95" />-->
<translate
android:duration="300"
android:fromXDelta="0"
android:startOffset="100"
android:toXDelta="100%p" />
<!--<scale-->
<!--android:duration="100"-->
<!--android:fillAfter="true"-->
<!--android:fromXScale="1.0"-->
<!--android:fromYScale="1.0"-->
<!--android:pivotX="50%"-->
<!--android:pivotY="50%"-->
<!--android:startOffset="600"-->
<!--android:toXScale="1.05"-->
<!--android:toYScale="1.05" />-->
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- 上下滑入式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="200"
android:fromYDelta="100%p"
android:toYDelta="0"
/>
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="200"
/>
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- 上下滑入式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="200"
android:fromYDelta="0"
android:toYDelta="50%p" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="200"
/>
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="20dp"/>
<solid android:color="@color/toast_checktrue_bg_color"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rl_idcard_cn_root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:fitsSystemWindows="true">
<TextureView
android:id="@+id/idcardscan_cn_layout_surface"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.megvii.demo.view.IDCardGuide
android:id="@+id/idcardscan_cn_layout_guide"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:focusable="false" />
<ImageView
android:id="@+id/iv_people_light_icon"
android:layout_width="347dp"
android:layout_height="220dp"
android:layout_centerInParent="true"
android:background="@mipmap/bg_people_light_icon"
android:visibility="gone" />
<ImageView
android:id="@+id/iv_china_light_icon"
android:layout_width="347dp"
android:layout_height="220dp"
android:layout_centerInParent="true"
android:background="@mipmap/bg_china_light_icon"
android:visibility="gone" />
<ImageView
android:id="@+id/ib_animal_breath_view"
android:layout_width="347dp"
android:layout_height="220dp"
android:layout_centerInParent="true"
android:background="@mipmap/bg_sfz_light"
android:visibility="gone" />
<ImageView
android:id="@+id/ib_animal_one_view"
android:layout_width="347dp"
android:layout_height="220dp"
android:layout_centerInParent="true"
android:background="@mipmap/bg_sfz_light"
android:visibility="gone" />
<TextView
android:id="@+id/tv_tip_text"
android:layout_width="286dp"
android:layout_height="30dp"
android:layout_centerInParent="true"
android:textColor="#FFFFFFFF"
android:textSize="20sp"
android:visibility="gone" />
<include layout="@layout/view_toast" />
<com.megvii.demo.view.IDCardGuideH
android:id="@+id/idcardscan_cn_layout_guide_h"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:focusable="false"
android:visibility="visible" />
<ImageView
android:id="@+id/iv_idcard_cn_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
<ImageView
android:id="@+id/iv_icon_tick_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/icon_tick"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/rl_megvii_idcard_cn_tips"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:background="@android:color/white"
android:visibility="visible">
<TextView
android:id="@+id/tv_megvii_idcard_cn_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/idcard_cn_dimen_30"
android:text="@string/idcard_cn_tips_face"
android:textColor="@color/idcard_cn_tips_text_color"
android:textSize="@dimen/idcard_cn_tips_text_size" />
</RelativeLayout>
<TextView
android:id="@+id/tv_megvii_idcard_cn_h_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/idcard_cn_horizontal_tips_margin_bottom"
android:text="@string/idcard_cn_tips_emblem"
android:textColor="@color/idcard_cn_tips_text_color"
android:textSize="@dimen/idcard_cn_tips_text_size"
android:visibility="gone" />
<include
android:id="@+id/in_idcard_cn_title_bar"
layout="@layout/view_title_bar" />
<RelativeLayout
android:id="@+id/rl_megvii_idcard_cn_title_bar_h"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/idcard_cn_dimen_10"
android:background="@android:color/transparent"
android:visibility="gone">
<RelativeLayout
android:id="@+id/rl_megvii_idcard_cn_goback_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_megvii_idcard_cn_bg_icon_h"
android:layout_width="@dimen/idcard_cn_gb_icon_width"
android:layout_height="@dimen/idcard_cn_gb_icon_height"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/idcard_cn_gb_icon_marg_left"
android:background="@mipmap/iv_megvii_idcard_cn_back_h" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/tv_megvii_idcard_cn_bg_icon_h"
android:text="返回"
android:textColor="@color/idcard_cn_white"
android:textSize="@dimen/idcard_cn_gb_text_size" />
</RelativeLayout>
<TextView
android:id="@+id/tv_megvii_idcard_cn_tips_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text=""
android:textColor="@color/idcard_cn_white"
android:textSize="@dimen/idcard_cn_gb_text_size" />
</RelativeLayout>
<ImageView
android:id="@+id/iv_idcard_cn_bottom_logo_h"
android:layout_width="105dp"
android:layout_height="12dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="@dimen/idcard_cn_dimen_20"
android:layout_marginBottom="@dimen/idcard_cn_dimen_10"
android:visibility="gone" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_height="match_parent">
<ImageView
android:id="@+id/result_face_image"
android:layout_width="100dp"
android:layout_marginTop="50dp"
android:layout_height="100dp"
android:scaleType="fitCenter" />
<ImageView
android:id="@+id/result_idcard_image"
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_marginTop="10dp"
android:scaleType="fitCenter" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/idcard_cn_title_bar_height"
android:background="@color/idcard_cn_white">
<RelativeLayout
android:id="@+id/rl_megvii_idcard_cn_goback"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_megvii_idcard_cn_bg_icon"
android:layout_width="@dimen/idcard_cn_gb_icon_width"
android:layout_height="@dimen/idcard_cn_gb_icon_height"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/idcard_cn_gb_icon_marg_left"
android:background="@mipmap/iv_megvii_idcard_cn_back" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/tv_megvii_idcard_cn_bg_icon"
android:text="返回"
android:textColor="@color/idcard_cn_gb_text_color"
android:textSize="@dimen/idcard_cn_gb_text_size" />
</RelativeLayout>
<TextView
android:id="@+id/tv_megvii_idcard_cn_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="识别结果"
android:textSize="@dimen/idcard_cn_gb_text_size"
android:textColor="@color/idcard_cn_tips_bg_color"
android:layout_centerInParent="true"
android:visibility="gone"
/>
<TextView
android:id="@+id/tv_megvii_idcard_cn_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textColor="#00B2E3"
android:textSize="@dimen/idcard_cn_gb_text_size"
android:gravity="center"
android:paddingLeft="@dimen/idcard_cn_dimen_15"
android:paddingRight="@dimen/idcard_cn_dimen_15"
android:text="重新拍摄"
android:layout_alignParentRight="true"
android:visibility="gone"
/>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_toast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@drawable/auth_toast_bg"
android:visibility="gone"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_auth_toast_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:visibility="gone"
android:background="@mipmap/icon_auth_toast_tip" />
<TextView
android:id="@+id/toast_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="40dp"
android:paddingTop="6dp"
android:paddingRight="40dp"
android:paddingBottom="6dp"
android:text="请将身份证四边与线框对齐"
android:textColor="@color/toast_checktrue_text_color"
android:textSize="@dimen/checktrue_toast_text_size" />
</RelativeLayout>
\ No newline at end of file
<resources>
<style name="Megvii_idcard_cn_SDKTheme" parent="@android:style/Theme.Holo.NoActionBar.TranslucentDecor">
</style>
<style name="idcard_cn_TranslutTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- values-v19下的主题 -->
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">false</item>
</style>
<style name="idcard_cn_FullScreenTheme" parent="@android:style/Theme.Holo.Light.NoActionBar.Fullscreen">
<!-- values-v19下的主题 -->
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">false</item>
</style>
</resources>
\ No newline at end of file
<resources>
<style name="idcard_cn_AppTheme" parent="@android:style/Theme.Holo.NoActionBar.TranslucentDecor">
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
<style name="idcard_cn_TranslutTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- values-v21下的主题 -->
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
<!-- android 5.0开始,需要把颜色设置成透明,否则状态栏会呈现系统默认的浅灰色 -->
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
<style name="idcard_cn_FullScreenTheme" parent="@android:style/Theme.Holo.Light.NoActionBar.Fullscreen">
<!-- values-v21下的主题 -->
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
<!-- android 5.0开始,需要把颜色设置成透明,否则状态栏会呈现系统默认的浅灰色 -->
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="idcard_cn_white">#FFFFFF</color>
<color name="idcard_cn_black">#000000</color>
<color name="idcard_cn_flash_bg_color">#FFFFFF</color>
<color name="idcard_cn_tips_bg_color">#282830</color>
<color name="idcard_cn_tips_text_color">#4A4A4A</color>
<color name="idcard_cn_toast_text_color">#FFFFFF</color>
<color name="idcard_cn_title_bar_bg_color">#FAFAFA</color>
<color name="idcard_cn_gb_text_color">#5FA3FA</color>
<color name="idcard_cn_falsh_page_text_color">#4A4A4A</color>
<color name="idcard_cn_toast_bg_color">#80000000</color>
<color name="idcard_cn_upload_btn_bg_color">#5FA3FA</color>
<color name="idcard_cn_info_text_color">#515666</color>
<color name="idcard_cn_press_color">#3171C2</color>
<color name="idcard_cn_agreement_unchecked_bt_color">#CFD6DD</color>
<color name="idcard_cn_blue">#51c3f6</color>
<color name="toast_checktrue_bg_color">#80000000</color>
<color name="toast_checktrue_text_color">#FFFFFF</color>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="idcard_cn_title_bar_height">44dp</dimen>
<dimen name="idcard_cn_flash_page_big_text">32sp</dimen>
<dimen name="idcard_cn_flash_page_big_text_margin">40dp</dimen>
<dimen name="idcard_cn_tips_bg_height">190dp</dimen>
<dimen name="idcard_cn_tips_text_size">32sp</dimen>
<dimen name="idcard_cn_tips_text_detail_size">14sp</dimen>
<dimen name="idcard_cn_toast_text_size">12sp</dimen>
<dimen name="idcard_cn_toast_text_padding_top">7dp</dimen>
<dimen name="idcard_cn_toast_text_padding_left">25dp</dimen>
<dimen name="idcard_cn_result_big_text_size">30sp</dimen>
<dimen name="idcard_cn_result_small_text_size">18sp</dimen>
<dimen name="idcard_cn_gb_icon_width">18dp</dimen>
<dimen name="idcard_cn_gb_icon_height">18dp</dimen>
<dimen name="idcard_cn_gb_icon_marg_left">16dp</dimen>
<dimen name="idcard_cn_gb_text_size">18sp</dimen>
<dimen name="idcard_cn_horizontal_tips_margin_bottom">24dp</dimen>
<dimen name="idcard_cn_result_item_height">50dp</dimen>
<dimen name="idcard_cn_info_text_size">14sp</dimen>
<dimen name="idcard_cn_warning_small_text_margin">10dp</dimen>
<dimen name="idcard_cn_dialog_content_margin_left">25dp</dimen>
<dimen name="idcard_cn_dialog_content_margin_top">15dp</dimen>
<dimen name="idcard_cn_dialog_line_margin_top">22dp</dimen>
<dimen name="idcard_cn_flash_image_width">258dp</dimen>
<dimen name="idcard_cn_flash_image_height">170dp</dimen>
<dimen name="idcard_cn_image_desc_text_size">14sp</dimen>
<dimen name="idcard_cn_falsh_page_start_btn_text">16sp</dimen>
<dimen name="idcard_cn_user_agree_text_size">12sp</dimen>
<dimen name="idcard_cn_user_agree_text_margin_left">8dp</dimen>
<dimen name="idcard_cn_check_box_size">13.5dip</dimen>
<dimen name="idcard_cn_start_bt_width">300dp</dimen>
<dimen name="idcard_cn_start_bt_height">44dp</dimen>
<dimen name="idcard_cn_start_bt_margin_bottom">80dp</dimen>
<dimen name="idcard_guide_round_rect_left">15dp</dimen>
<dimen name="idcard_guide_landscape_round_rect_width">398dp</dimen>
<dimen name="idcard_guide_landscape_round_rect_height">254dp</dimen>
<dimen name="idcard_cn_dimen_20">20dp</dimen>
<dimen name="idcard_cn_dimen_10">10dp</dimen>
<dimen name="idcard_cn_dimen_15">15dp</dimen>
<dimen name="idcard_cn_dimen_30">30dp</dimen>
<dimen name="idcard_cn_dimen_16">16dp</dimen>
<dimen name="idcard_cn_dimen_90">90dp</dimen>
<dimen name="idcard_cn_dimen_28">28dp</dimen>
<dimen name="idcard_cn_dimen_4">4dp</dimen>
<dimen name="idcard_cn_dimen_60">60dp</dimen>
<dimen name="idcard_cn_dimen_1">1dp</dimen>
<dimen name="idcard_cn_dimen_3">3dp</dimen>
<dimen name="idcard_cn_dimen_5">5dp</dimen>
<dimen name="dimen_1">1dp</dimen>
<dimen name="dimen_2">2dp</dimen>
<dimen name="dimen_5">5dp</dimen>
<dimen name="dimen_3">3dp</dimen>
<dimen name="checktrue_toast_text_size">16sp</dimen>
<dimen name="checktrue_bg_tick_bg_height">35dp</dimen>
<dimen name="checktrue_bg_tick_top_margin">32dp</dimen>
<dimen name="checktrue_bg_tick_left_margin">28dp</dimen>
<dimen name="checktrue_toast_text_padding_height">10dp</dimen>
<dimen name="idcard_checktrue_rect_big_offset">16dp</dimen>
<dimen name="idcard_checktrue_rect_little_offset">8dp</dimen>
<dimen name="checktrue_title_bar_height">44dp</dimen>
<dimen name="dimen_90">90dp</dimen>
</resources>
\ No newline at end of file
<resources>
<string name="app_name">身份证质量检测</string>
<string name="idcard_cn_tips_face">人像面拍摄</string>
<string name="idcard_cn_tips_face_detial">请将身份证人像面放在框内</string>
<string name="idcard_cn_tips_emblem">国徽面拍摄</string>
<string name="idcard_cn_tips_emblem_detial">请将身份证国徽面放在框内</string>
<string name="remind_idcard_quality_failed_1">未检测到身份证</string>
<string name="remind_idcard_quality_failed_2">请对齐线框</string>
<string name="remind_idcard_quality_failed_2_1">请再次对齐线框</string>
<string name="remind_idcard_quality_failed_3">图像不清晰,请调整拍摄</string>
<string name="remind_idcard_quality_failed_4">存在反光,请调整拍摄</string>
<string name="remind_idcard_quality_failed_5">存在阴影,请调整拍摄</string>
<string name="remind_idcard_quality_failed_6">请拍摄人像面</string>
<string name="remind_idcard_quality_failed_7">请拍摄国徽面</string>
<string name="remind_idcard_quality_failed_8">身份证倒置,请放正</string>
<string name="tip_good_again">很好,请再次对齐</string>
<string name="idcard_cn_upload">确认并上传</string>
<string name="idcard_cn_re_shoot">重新拍摄</string>
<string name="idcard_cn_warning_text">识别失败</string>
<string name="idcard_cn_warning_small_text">请注意拍摄角度及光线,并确保身份证两面文字清晰</string>
<string name="idcard_cn_dialog_content">拍摄时请将身份证放入框内,请注意拍摄角度及光线,并确保身份证两面文字清晰</string>
<string name="idcard_cn_agree_toast_text">请先同意并勾选《FaceID用户协议》</string>
<string name="idcard_cn_guide_text_double_side">即将进行身份证人像面和国徽面的扫描拍摄</string>
<string name="idcard_cn_guide_text_front_side">即将进行身份证人像面的扫描拍摄</string>
<string name="idcard_cn_guide_text_back_side">即将进行身份证国徽面的扫描拍摄</string>
<string name="tip_rect_first_tip">请保证身份证边缘与线框对齐</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="idcard_cn_Transparent">
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="idcard_cn_TranslutTheme" parent="Megvii_idcard_cn_SDKTheme">
<!-- values下的主题 在4.4之前的版本,设置不了。直接跟随系统主题 -->
</style>
<style name="IDCard_CN_NoActionBar.Slide" parent="idcard_cn_TranslutTheme">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@style/IDCard.CN.DarkActionBar.Slide.Animation</item>
</style>
<style name="idcard_cn_FullScreenTheme" parent="@android:style/Theme.Holo.Light.NoActionBar.Fullscreen">
</style>
<style name="idcard_info_style">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textColor">@color/idcard_cn_info_text_color</item>
<item name="android:textSize">@dimen/idcard_cn_info_text_size</item>
<item name="android:layout_centerVertical">true</item>
</style>
<style name="IDCard_CN_AnimBottom" parent="@android:style/Animation">
<item name="android:windowEnterAnimation">@anim/idcard_cn_push_bottom_in</item>
<item name="android:windowExitAnimation">@anim/idcard_cn_push_bottom_out</item>
</style>
<style name="IDCard.CN.DarkActionBar.Slide.Animation" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/idcard_cn_mg_slide_in_left</item>
<item name="android:activityOpenExitAnimation">@anim/idcard_cn_mg_slide_out_left</item>
<item name="android:activityCloseEnterAnimation">@anim/idcard_cn_mg_slide_in_left</item>
<item name="android:activityCloseExitAnimation">@anim/idcard_cn_mg_slide_out_left</item>
</style>
</resources>
\ No newline at end of file
package com.megvii.demo;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
\ No newline at end of file
......@@ -10,4 +10,3 @@ include ':Components:xbirdbusiness'
//setBinding(new Binding([gradle: this]))
//evaluate(new File("../YDSubway_flutter/.android", 'include_flutter.groovy'))
include ':Components:userinfo'
include ':idcard_quality'
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