Commit 2e070f771351333af5cc3022df3cce36d23b26fa

Authored by hhh
1 parent 88aea392

身份证采集页面UI开发

Showing 67 changed files with 4750 additions and 26 deletions

Too many changes to show.

To preserve performance only 67 of 76 files are displayed.

.idea/misc.xml
... ... @@ -39,7 +39,7 @@
39 39 </value>
40 40 </option>
41 41 </component>
42   - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
  42 + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
43 43 <output url="file://$PROJECT_DIR$/build/classes" />
44 44 </component>
45 45 <component name="ProjectType">
... ...
app/strike/build.gradle
... ... @@ -187,5 +187,6 @@ dependencies {
187 187 // implementation project(path: ':core:encipher')
188 188 // implementation project(path: ':cloud:user')
189 189 implementation project(path: ':cloud:wjj_lib')
  190 + implementation project(path: ':core:dentity_verfication')
190 191  
191 192 }
... ...
app/strike/src/main/AndroidManifest.xml
... ... @@ -17,7 +17,8 @@
17 17 android:roundIcon="@mipmap/ic_launcher_round"
18 18 android:supportsRtl="true"
19 19 android:theme="@style/AppTheme">
20   - <activity android:name="com.cnlive.strike.imgutil.SimplePrevActivity"></activity>
  20 + <activity android:name="com.cnlive.strike.imgutil.UgcActivity"></activity>
  21 + <activity android:name="com.cnlive.strike.imgutil.SimplePrevActivity" />
21 22 <activity android:name="com.cnlive.strike.demo.activity.HappyChinaActivity" />
22 23  
23 24 <provider
... ... @@ -54,7 +55,6 @@
54 55 </intent-filter>
55 56 </activity>
56 57 <activity android:name="com.cnlive.strike.demo.activity.DemoMvpActivity" />
57   -
58 58 <activity android:name="com.cnlive.strike.demo.activity.DemoBottomSheetActivity" />
59 59  
60 60 <meta-data
... ...
app/strike/src/main/java/com/cnlive/strike/MainActivity.java
... ... @@ -10,6 +10,7 @@ import android.view.View;
10 10 import com.cnlive.cloud.R;
11 11 import com.cnlive.strike.demo.activity.DemoMvpActivity;
12 12 import com.cnlive.strike.imgutil.ImgActivity;
  13 +import com.cnlive.strike.imgutil.UgcActivity;
13 14  
14 15 import androidx.annotation.NonNull;
15 16 import androidx.annotation.Nullable;
... ... @@ -30,6 +31,7 @@ public class MainActivity extends AppCompatActivity {
30 31 }
31 32 });
32 33 findViewById(R.id.to_img).setOnClickListener(view -> startActivity(new Intent(this, ImgActivity.class)));
  34 + findViewById(R.id.ugc_btn).setOnClickListener(view -> startActivity(new Intent(this, UgcActivity.class)));
33 35 }
34 36  
35 37 @Nullable
... ...
app/strike/src/main/java/com/cnlive/strike/imgutil/UgcActivity.java 0 → 100644
  1 +package com.cnlive.strike.imgutil;
  2 +
  3 +import android.content.Intent;
  4 +import android.os.Bundle;
  5 +
  6 +import com.cnlive.cloud.R;
  7 +import com.cnlive.core.people_verfication.activity.TakeIdentityPictureActivity;
  8 +
  9 +import androidx.appcompat.app.AppCompatActivity;
  10 +
  11 +public class UgcActivity extends AppCompatActivity {
  12 +
  13 + @Override
  14 + protected void onCreate(Bundle savedInstanceState) {
  15 + super.onCreate(savedInstanceState);
  16 + setContentView(R.layout.activity_ugc);
  17 + findViewById(R.id.people_denty).setOnClickListener(view -> startActivity(new Intent(this, TakeIdentityPictureActivity.class)));
  18 + }
  19 +}
... ...
app/strike/src/main/res/layout/activity_main.xml
... ... @@ -20,5 +20,12 @@
20 20 android:layout_width="wrap_content"
21 21 android:layout_height="wrap_content"
22 22 android:text="图片加载测试"/>
  23 +
  24 + <Button
  25 + android:id="@+id/ugc_btn"
  26 + android:layout_width="wrap_content"
  27 + android:layout_height="wrap_content"
  28 + android:text="ugc直播认证"
  29 + />
23 30 </LinearLayout>
24 31 </layout>
25 32 \ No newline at end of file
... ...
app/strike/src/main/res/layout/activity_ugc.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + xmlns:app="http://schemas.android.com/apk/res-auto"
  4 + xmlns:tools="http://schemas.android.com/tools"
  5 + android:layout_width="match_parent"
  6 + android:layout_height="match_parent"
  7 + tools:context="com.cnlive.strike.imgutil.UgcActivity">
  8 +
  9 + <Button
  10 + android:id="@+id/people_denty"
  11 + android:layout_width="wrap_content"
  12 + android:layout_height="wrap_content"
  13 + android:text="个人认证"
  14 + />
  15 +</LinearLayout>
... ...
core/base/src/main/res/drawable-hdpi/top_back_black.webp 0 → 100644
No preview for this file type
core/base/src/main/res/drawable-xhdpi/top_back_black.webp 0 → 100644
No preview for this file type
core/base/src/main/res/drawable-xxhdpi/top_back_black.webp 0 → 100644
No preview for this file type
core/base/src/main/res/values/ids.xml
... ... @@ -7,4 +7,5 @@
7 7 <item name="isexpand" type="id"></item>
8 8 <!-- -->
9 9 <item name="rightBtn" type="id" />
  10 + <item name="fragment_back" type="id" />
10 11 </resources>
11 12 \ No newline at end of file
... ...
core/dentity_verfication/build.gradle
... ... @@ -5,11 +5,12 @@ android {
5 5 }
6 6  
7 7 dependencies {
8   - implementation fileTree(dir: 'libs', include: ['*.jar'])
9   -
  8 + implementation fileTree(include: ['*.jar'], dir: 'libs')
10 9 implementation 'androidx.appcompat:appcompat:1.0.2'
11 10 implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
12 11 testImplementation 'junit:junit:4.12'
13 12 androidTestImplementation 'androidx.test.ext:junit:1.1.1'
14 13 androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
  14 + implementation "com.cnlive.libs:mediapicker:$rootProject.libMediaPicker"
  15 + implementation files('libs/license.jar')
15 16 }
... ...
core/dentity_verfication/libs/license.jar 0 → 100644
No preview for this file type
core/dentity_verfication/libs/ocr-sdk.jar 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/AndroidManifest.xml
... ... @@ -2,8 +2,23 @@
2 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 3 package="com.cnlive.core.people_verfication">
4 4  
  5 + <uses-permission android:name="android.permission.CAMERA" />
  6 + <uses-permission android:name="android.permission.INTERNET" />
  7 + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  8 + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  9 +
  10 + <uses-feature android:name="android.hardware.camera2.full" />
  11 + <uses-feature android:name="android.hardware.camera" />
  12 + <uses-feature android:name="android.hardware.camera.autofocus" />
  13 +
5 14 <application>
6   - <activity android:name=".TakePictureActivity"></activity>
  15 + <activity android:name=".activity.TakeIdentityPictureActivity"></activity>
  16 + <activity
  17 + android:name="com.cnlive.core.ocr.ui.camera.CameraActivity"
  18 + android:configChanges="screenSize|orientation"
  19 + android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"
  20 + android:launchMode="singleTop"
  21 + android:screenOrientation="portrait"/>
7 22 </application>
8 23  
9 24 </manifest>
10 25 \ No newline at end of file
... ...
core/dentity_verfication/src/main/assets/models/integrity_model_secret.bin 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/assets/models/quality_model_secret.bin 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/RecognizeService.java 0 → 100644
  1 +package com.cnlive.core.ocr;
  2 +
  3 +import android.content.Context;
  4 +import android.util.Log;
  5 +
  6 +import com.baidu.ocr.sdk.OCR;
  7 +import com.baidu.ocr.sdk.OnResultListener;
  8 +import com.baidu.ocr.sdk.exception.OCRError;
  9 +import com.baidu.ocr.sdk.model.AccessToken;
  10 +import com.baidu.ocr.sdk.model.BankCardParams;
  11 +import com.baidu.ocr.sdk.model.BankCardResult;
  12 +import com.baidu.ocr.sdk.model.IDCardParams;
  13 +import com.baidu.ocr.sdk.model.IDCardResult;
  14 +
  15 +import java.io.File;
  16 +
  17 +/**
  18 + * @author Lynn
  19 + * @date 2018/1/4
  20 + */
  21 +
  22 +public class RecognizeService {
  23 +
  24 + /**
  25 + * 识别银行卡
  26 + *
  27 + * @param filePath 文件路径
  28 + * @param listener 回调
  29 + */
  30 + public static void recBankCard(String filePath, OnResultListener<BankCardResult> listener) {
  31 + BankCardParams params = new BankCardParams();
  32 + params.setImageFile(new File(filePath));
  33 + OCR.getInstance().recognizeBankCard(params, listener);
  34 + }
  35 +
  36 + /**
  37 + * 识别身份证
  38 + *
  39 + * @param idCardSide 身份证正反面
  40 + * @param filePath 文件路径
  41 + * @param listener 回调
  42 + */
  43 + public static void recIDCard(String idCardSide, String filePath, OnResultListener<IDCardResult> listener) {
  44 + IDCardParams param = new IDCardParams();
  45 + param.setImageFile(new File(filePath));
  46 + // 设置身份证正反面
  47 + param.setIdCardSide(idCardSide);
  48 + // 设置方向检测
  49 + param.setDetectDirection(true);
  50 + // 设置图像参数压缩质量0-100, 越大图像质量越好但是请求时间越长。 不设置则默认值为20
  51 + param.setImageQuality(20);
  52 + OCR.getInstance().recognizeIDCard(param, listener);
  53 + }
  54 +
  55 + /**
  56 + * 初始化 with AK & SK
  57 + *
  58 + * @param context context
  59 + */
  60 + public static void initAccessTokenWithAkSk(final Context context) {
  61 + OCR.getInstance().initAccessTokenWithAkSk(new OnResultListener<AccessToken>() {
  62 + @Override
  63 + public void onResult(AccessToken result) {
  64 + String token = result.getAccessToken();
  65 + Log.e("LynnTest", result.toString());
  66 + }
  67 +
  68 + @Override
  69 + public void onError(OCRError error) {
  70 + error.printStackTrace();
  71 + Log.e("LynnTest", error.getMessage());
  72 + }
  73 + }, context, "YIGsVzdh35y3GCaNUDsxM3cC", "neAe1orXgeTaiHkAbygrgCevKe1fPrG1");
  74 + }
  75 +
  76 + /**
  77 + * 初始化 授权文件(安全模式)
  78 + *
  79 + * @param context
  80 + */
  81 + public static void initAccessToken(Context context) {
  82 + OCR.getInstance().initAccessToken(new OnResultListener<AccessToken>() {
  83 + @Override
  84 + public void onResult(AccessToken accessToken) {
  85 + String token = accessToken.getAccessToken();
  86 + }
  87 +
  88 + @Override
  89 + public void onError(OCRError ocrError) {
  90 +
  91 + }
  92 + }, context);
  93 + }
  94 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/idcardquality/IDcardQualityProcess.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.idcardquality;
  5 +
  6 +import android.content.res.AssetManager;
  7 +import android.graphics.Bitmap;
  8 +
  9 +import com.baidu.idl.authority.AlgorithmOnMainThreadException;
  10 +import com.baidu.idl.authority.IDLAuthorityException;
  11 +import com.baidu.idl.license.License;
  12 +import com.baidu.idl.util.UIThread;
  13 +
  14 +
  15 +public class IDcardQualityProcess {
  16 +
  17 + private static IDcardQualityProcess mInstance;
  18 + private static String tokenString;
  19 + private static int mAuthorityStatus;
  20 + private static Throwable loadNativeException = null;
  21 + private static volatile boolean hasReleased;
  22 +
  23 + public IDcardQualityProcess() {
  24 + }
  25 +
  26 + public static synchronized IDcardQualityProcess getInstance() {
  27 + if (null == mInstance) {
  28 + mInstance = new IDcardQualityProcess();
  29 + }
  30 +
  31 + return mInstance;
  32 + }
  33 +
  34 + public native byte[] convertRGBImage(int[] colors, int width, int height);
  35 +
  36 + public native int idcardQualityModelInit(AssetManager var1, String var2);
  37 +
  38 + public native int idcardQualityCaptchaRelease();
  39 +
  40 + public native int idcardQualityProcess(byte[] var1, int var2, int var3, boolean var4, int var5);
  41 +
  42 + public static synchronized int init(String token) throws AlgorithmOnMainThreadException, IDLAuthorityException {
  43 + if (UIThread.isUITread()) {
  44 + throw new AlgorithmOnMainThreadException();
  45 + } else {
  46 + tokenString = token;
  47 +
  48 + try {
  49 + mAuthorityStatus = License.getInstance().init(tokenString);
  50 + } catch (Exception var2) {
  51 + if (var2!=null)
  52 + var2.printStackTrace();
  53 + }
  54 +
  55 + return mAuthorityStatus;
  56 + }
  57 + }
  58 +
  59 + public int idcardQualityInit(AssetManager assetManager, String modelPath) {
  60 + if (mAuthorityStatus == 0) {
  61 + hasReleased = false;
  62 + return this.idcardQualityModelInit(assetManager, modelPath);
  63 + } else {
  64 + return mAuthorityStatus;
  65 + }
  66 + }
  67 +
  68 + public int idcardQualityRelease() {
  69 + if (mAuthorityStatus == 0) {
  70 + hasReleased = true;
  71 + this.idcardQualityCaptchaRelease();
  72 + return 0;
  73 + } else {
  74 + return mAuthorityStatus;
  75 + }
  76 + }
  77 +
  78 + public int idcardQualityDetectionImg(Bitmap img, boolean isfont) {
  79 + if (mAuthorityStatus == 0) {
  80 + if (hasReleased) {
  81 + return -1;
  82 + }
  83 + int imgHeight = img.getHeight();
  84 + int imgWidth = img.getWidth();
  85 + byte[] imageData = this.getRGBImageData(img);
  86 + return this.idcardQualityProcess(imageData, imgHeight, imgWidth, isfont, 3);
  87 + } else {
  88 + return mAuthorityStatus;
  89 + }
  90 + }
  91 +
  92 + public static Throwable getLoadSoException() {
  93 + return loadNativeException;
  94 + }
  95 +
  96 + public byte[] getRGBImageData(Bitmap img) {
  97 + int imgWidth = img.getWidth();
  98 + int imgHeight = img.getHeight();
  99 + int[] pixels = new int[imgWidth * imgHeight];
  100 + img.getPixels(pixels, 0, imgWidth, 0, 0, imgWidth, imgHeight);
  101 + byte[] imageData = convertRGBImage(pixels, imgWidth, imgHeight);
  102 + return imageData;
  103 + }
  104 +
  105 + public void releaseModel() {
  106 + this.idcardQualityRelease();
  107 + }
  108 +
  109 + static {
  110 + try {
  111 + System.loadLibrary("idl_license");
  112 + System.loadLibrary("idcard_quality.1.1.1");
  113 + } catch (Throwable e) {
  114 + loadNativeException = e;
  115 + }
  116 + mInstance = null;
  117 + mAuthorityStatus = 256;
  118 + }
  119 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/camera/Camera1Control.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.camera;
  5 +
  6 +import android.Manifest;
  7 +import android.content.Context;
  8 +import android.content.pm.FeatureInfo;
  9 +import android.content.pm.PackageManager;
  10 +import android.graphics.ImageFormat;
  11 +import android.graphics.Rect;
  12 +import android.graphics.SurfaceTexture;
  13 +import android.graphics.YuvImage;
  14 +import android.hardware.Camera;
  15 +import android.os.Build;
  16 +import android.util.Log;
  17 +import android.view.TextureView;
  18 +import android.view.View;
  19 +import android.widget.FrameLayout;
  20 +
  21 +import com.cnlive.core.libs.base.util.AlertUtil;
  22 +
  23 +import java.io.ByteArrayOutputStream;
  24 +import java.io.IOException;
  25 +import java.util.ArrayList;
  26 +import java.util.Collections;
  27 +import java.util.Comparator;
  28 +import java.util.List;
  29 +import java.util.concurrent.atomic.AtomicBoolean;
  30 +
  31 +import androidx.core.app.ActivityCompat;
  32 +
  33 +/**
  34 + * 5.0以下相机API的封装。
  35 + */
  36 +@SuppressWarnings("deprecation")
  37 +public class Camera1Control implements ICameraControl {
  38 +
  39 + private int displayOrientation = 0;
  40 + private int cameraId = 0;
  41 + private int flashMode;
  42 + private AtomicBoolean takingPicture = new AtomicBoolean(false);
  43 + private AtomicBoolean abortingScan = new AtomicBoolean(false);
  44 + private Context context;
  45 + private Camera camera;
  46 +
  47 + private Camera.Parameters parameters;
  48 + private PermissionCallback permissionCallback;
  49 + private Rect previewFrame = new Rect();
  50 +
  51 + private PreviewView previewView;
  52 + private View displayView;
  53 + private int rotation = 0;
  54 + private OnDetectPictureCallback detectCallback;
  55 + private int previewFrameCount = 0;
  56 + private Camera.Size optSize;
  57 +
  58 + /*
  59 + * 非扫描模式
  60 + */
  61 + private final int MODEL_NOSCAN = 0;
  62 +
  63 + /*
  64 + * 本地质量控制扫描模式
  65 + */
  66 + private final int MODEL_SCAN = 1;
  67 +
  68 + private int detectType = MODEL_NOSCAN;
  69 +
  70 + public int getCameraRotation() {
  71 + return rotation;
  72 + }
  73 +
  74 + public AtomicBoolean getAbortingScan() {
  75 + return abortingScan;
  76 + }
  77 +
  78 + @Override
  79 + public void setDetectCallback(OnDetectPictureCallback callback) {
  80 + detectType = MODEL_SCAN;
  81 + detectCallback = callback;
  82 + }
  83 +
  84 + private void onRequestDetect(byte[] data) {
  85 + // 相机已经关闭
  86 + if (camera == null || data == null) {
  87 + return;
  88 + }
  89 +
  90 + YuvImage img = new YuvImage(data, ImageFormat.NV21, optSize.width, optSize.height, null);
  91 + ByteArrayOutputStream os = null;
  92 + try {
  93 + os = new ByteArrayOutputStream(data.length);
  94 + img.compressToJpeg(new Rect(0, 0, optSize.width, optSize.height), 80, os);
  95 + byte[] jpeg = os.toByteArray();
  96 + int status = detectCallback.onDetect(jpeg, getCameraRotation());
  97 +
  98 + if (status == 0) {
  99 + clearPreviewCallback();
  100 + }
  101 + } catch (OutOfMemoryError e) {
  102 + // 内存溢出则取消当次操作
  103 + } finally {
  104 + try {
  105 + os.close();
  106 + } catch (Throwable e) {
  107 + e.printStackTrace();
  108 + }
  109 + }
  110 +
  111 + }
  112 +
  113 + @Override
  114 + public void setDisplayOrientation(@CameraView.Orientation int displayOrientation) {
  115 + this.displayOrientation = displayOrientation;
  116 + switch (displayOrientation) {
  117 + case CameraView.ORIENTATION_PORTRAIT:
  118 + rotation = 90;
  119 + break;
  120 + case CameraView.ORIENTATION_HORIZONTAL:
  121 + rotation = 0;
  122 + break;
  123 + case CameraView.ORIENTATION_INVERT:
  124 + rotation = 180;
  125 + break;
  126 + default:
  127 + rotation = 0;
  128 + }
  129 + previewView.requestLayout();
  130 + }
  131 +
  132 + /**
  133 + * {@inheritDoc}
  134 + */
  135 + @Override
  136 + public void refreshPermission() {
  137 + startPreview(true);
  138 + }
  139 +
  140 + /**
  141 + * {@inheritDoc}
  142 + */
  143 + @Override
  144 + public void setFlashMode(@FlashMode int flashMode) {
  145 + if (this.flashMode == flashMode) {
  146 + return;
  147 + }
  148 + this.flashMode = flashMode;
  149 + updateFlashMode(flashMode);
  150 + }
  151 +
  152 + @Override
  153 + public int getFlashMode() {
  154 + return flashMode;
  155 + }
  156 +
  157 + @Override
  158 + public void start() {
  159 +
  160 + }
  161 +
  162 + @Override
  163 + public void stop() {
  164 + if (camera != null) {
  165 + camera.setPreviewCallback(null);
  166 + stopPreview();
  167 + // 避免同步代码,为了先设置null后release
  168 + Camera tempC = camera;
  169 + camera = null;
  170 + tempC.release();
  171 + camera = null;
  172 + buffer = null;
  173 + }
  174 + }
  175 +
  176 + private void stopPreview() {
  177 + if (camera != null) {
  178 + camera.stopPreview();
  179 + }
  180 + }
  181 +
  182 + @Override
  183 + public void pause() {
  184 + if (camera != null) {
  185 + stopPreview();
  186 + }
  187 + setFlashMode(FLASH_MODE_OFF);
  188 + }
  189 +
  190 + @Override
  191 + public void resume() {
  192 + takingPicture.set(false);
  193 + if (camera == null) {
  194 + openCamera();
  195 + } else {
  196 + previewView.textureView.setSurfaceTextureListener(surfaceTextureListener);
  197 + if (previewView.textureView.isAvailable()) {
  198 + startPreview(false);
  199 + }
  200 + }
  201 + }
  202 +
  203 + @Override
  204 + public View getDisplayView() {
  205 + return displayView;
  206 + }
  207 +
  208 + @Override
  209 + public void takePicture(final OnTakePictureCallback onTakePictureCallback) {
  210 + if (takingPicture.get()) {
  211 + return;
  212 + }
  213 + try {
  214 + switch (displayOrientation) {
  215 + case CameraView.ORIENTATION_PORTRAIT:
  216 + parameters.setRotation(90);
  217 + break;
  218 + case CameraView.ORIENTATION_HORIZONTAL:
  219 + parameters.setRotation(0);
  220 + break;
  221 + case CameraView.ORIENTATION_INVERT:
  222 + parameters.setRotation(180);
  223 + break;
  224 + }
  225 + Camera.Size picSize = getOptimalSize(camera.getParameters().getSupportedPictureSizes());
  226 + parameters.setPictureSize(picSize.width, picSize.height);
  227 + camera.setParameters(parameters);
  228 + takingPicture.set(true);
  229 +
  230 + camera.takePicture(null, null, new Camera.PictureCallback() {
  231 + @Override
  232 + public void onPictureTaken(byte[] data, Camera camera) {
  233 + startPreview(false);
  234 + takingPicture.set(false);
  235 + if (onTakePictureCallback != null) {
  236 + onTakePictureCallback.onPictureTaken(data);
  237 + }
  238 + }
  239 + });
  240 + } catch (RuntimeException e) {
  241 + e.printStackTrace();
  242 + startPreview(false);
  243 + ;
  244 + takingPicture.set(false);
  245 + }
  246 + }
  247 +
  248 + @Override
  249 + public void setPermissionCallback(PermissionCallback callback) {
  250 + this.permissionCallback = callback;
  251 + }
  252 +
  253 + public Camera1Control(Context context) {
  254 + this.context = context;
  255 + previewView = new PreviewView(context);
  256 + openCamera();
  257 + }
  258 +
  259 + private void openCamera() {
  260 + setupDisplayView();
  261 + }
  262 +
  263 + private void setupDisplayView() {
  264 + final TextureView textureView = new TextureView(context);
  265 + previewView.textureView = textureView;
  266 + previewView.setTextureView(textureView);
  267 + displayView = previewView;
  268 + textureView.setSurfaceTextureListener(surfaceTextureListener);
  269 + }
  270 +
  271 + private SurfaceTexture surfaceCache;
  272 +
  273 + private byte[] buffer = null;
  274 +
  275 + private void setPreviewCallbackImpl() {
  276 + if (buffer == null) {
  277 + buffer = new byte[displayView.getWidth()
  278 + * displayView.getHeight() * ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8];
  279 + }
  280 + if (camera != null && detectType == MODEL_SCAN) {
  281 + camera.addCallbackBuffer(buffer);
  282 + camera.setPreviewCallback(previewCallback);
  283 + }
  284 + }
  285 +
  286 + private void clearPreviewCallback() {
  287 + if (camera != null && detectType == MODEL_SCAN) {
  288 + camera.setPreviewCallback(null);
  289 + stopPreview();
  290 + }
  291 + }
  292 +
  293 + Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
  294 + @Override
  295 + public void onPreviewFrame(final byte[] data, Camera camera) {
  296 + // 扫描成功阻止打开新线程处理
  297 + if (abortingScan.get()) {
  298 + return;
  299 + }
  300 + // 节流
  301 + if (previewFrameCount++ % 5 != 0) {
  302 + return;
  303 + }
  304 +
  305 + // 在某些机型和某项项目中,某些帧的data的数据不符合nv21的格式,需要过滤,否则后续处理会导致crash
  306 + if (data.length != parameters.getPreviewSize().width * parameters.getPreviewSize().height * 1.5) {
  307 + return;
  308 + }
  309 +
  310 + camera.addCallbackBuffer(buffer);
  311 +
  312 + CameraThreadPool.execute(new Runnable() {
  313 + @Override
  314 + public void run() {
  315 + Camera1Control.this.onRequestDetect(data);
  316 + }
  317 + });
  318 + }
  319 + };
  320 +
  321 +
  322 + private void initCamera() {
  323 + try {
  324 + if (camera == null) {
  325 + Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
  326 + for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
  327 + Camera.getCameraInfo(i, cameraInfo);
  328 + if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
  329 + cameraId = i;
  330 + }
  331 + }
  332 + try {
  333 + camera = Camera.open(cameraId);
  334 + } catch (Throwable e) {
  335 + e.printStackTrace();
  336 + startPreview(true);
  337 + return;
  338 + }
  339 +
  340 + }
  341 + if (parameters == null) {
  342 + parameters = camera.getParameters();
  343 + parameters.setPreviewFormat(ImageFormat.NV21);
  344 + }
  345 + opPreviewSize(previewView.getWidth(), previewView.getHeight());
  346 + camera.setPreviewTexture(surfaceCache);
  347 + setPreviewCallbackImpl();
  348 + startPreview(false);
  349 + } catch (IOException e) {
  350 + e.printStackTrace();
  351 + }
  352 + }
  353 +
  354 + private TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
  355 + @Override
  356 + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
  357 + surfaceCache = surface;
  358 + initCamera();
  359 + }
  360 +
  361 + @Override
  362 + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
  363 + opPreviewSize(previewView.getWidth(), previewView.getHeight());
  364 + setPreviewCallbackImpl();
  365 + }
  366 +
  367 + @Override
  368 + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
  369 + return false;
  370 + }
  371 +
  372 + @Override
  373 + public void onSurfaceTextureUpdated(SurfaceTexture surface) {
  374 + setPreviewCallbackImpl();
  375 + }
  376 + };
  377 +
  378 + // 开启预览
  379 + private void startPreview(boolean checkPermission) {
  380 + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
  381 + != PackageManager.PERMISSION_GRANTED) {
  382 + if (checkPermission && permissionCallback != null) {
  383 + permissionCallback.onRequestPermission();
  384 + }
  385 + return;
  386 + }
  387 + if (camera == null) {
  388 + initCamera();
  389 + } else {
  390 + try {
  391 + camera.startPreview();
  392 + CameraThreadPool.createAutoFocusTimerTask(new Runnable() {
  393 + @Override
  394 + public void run() {
  395 + synchronized (Camera1Control.this) {
  396 + if (camera != null && !takingPicture.get()) {
  397 + try {
  398 + camera.autoFocus(new Camera.AutoFocusCallback() {
  399 + @Override
  400 + public void onAutoFocus(boolean success, Camera camera) {
  401 + }
  402 + });
  403 + } catch (Throwable e) {
  404 + // startPreview是异步实现,可能在某些机器上前几次调用会autofocus failß
  405 + }
  406 + }
  407 + }
  408 + }
  409 + });
  410 + } catch (Exception e) {
  411 + Log.e("ocr_ui startPreview", "startPreview: " + (e == null ? "" : e.getMessage()));
  412 + }
  413 + }
  414 + }
  415 +
  416 + private void opPreviewSize(int width, @SuppressWarnings("unused") int height) {
  417 +
  418 + if (parameters != null && camera != null && width > 0) {
  419 + try {
  420 + optSize = getOptimalSize(camera.getParameters().getSupportedPreviewSizes());
  421 + }catch (Exception e){
  422 + e.printStackTrace();
  423 + }
  424 + optSize = getOptimalSize(camera.getParameters().getSupportedPreviewSizes());
  425 + parameters.setPreviewSize(optSize.width, optSize.height);
  426 + previewView.setRatio(1.0f * optSize.width / optSize.height);
  427 +
  428 + camera.setDisplayOrientation(getSurfaceOrientation());
  429 + stopPreview();
  430 + try {
  431 + camera.setParameters(parameters);
  432 + } catch (RuntimeException e) {
  433 + e.printStackTrace();
  434 +
  435 + }
  436 + startPreview(false);
  437 + }
  438 + }
  439 +
  440 + private Camera.Size getOptimalSize(List<Camera.Size> sizes) {
  441 + int width = previewView.textureView.getWidth();
  442 + int height = previewView.textureView.getHeight();
  443 + Camera.Size pictureSize = sizes.get(0);
  444 +
  445 + List<Camera.Size> candidates = new ArrayList<>();
  446 +
  447 + for (Camera.Size size : sizes) {
  448 + if (size.width >= width && size.height >= height && size.width * height == size.height * width) {
  449 + // 比例相同
  450 + candidates.add(size);
  451 + } else if (size.height >= width && size.width >= height && size.width * width == size.height * height) {
  452 + // 反比例
  453 + candidates.add(size);
  454 + }
  455 + }
  456 + if (!candidates.isEmpty()) {
  457 + return Collections.min(candidates, sizeComparator);
  458 + }
  459 +
  460 + for (Camera.Size size : sizes) {
  461 + if (size.width > width && size.height > height) {
  462 + return size;
  463 + }
  464 + }
  465 +
  466 + return pictureSize;
  467 + }
  468 +
  469 + private Comparator<Camera.Size> sizeComparator = new Comparator<Camera.Size>() {
  470 + @Override
  471 + public int compare(Camera.Size lhs, Camera.Size rhs) {
  472 + return Long.signum((long) lhs.width * lhs.height - (long) rhs.width * rhs.height);
  473 + }
  474 + };
  475 +
  476 + private void updateFlashMode(int flashMode) {
  477 + switch (flashMode) {
  478 + case FLASH_MODE_TORCH:
  479 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  480 + try {
  481 + parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
  482 + } catch (Exception e) {
  483 + e.printStackTrace();
  484 + }
  485 + }else {
  486 + boolean isUse = false;
  487 + PackageManager packageManager = context.getPackageManager();
  488 + FeatureInfo[] features = packageManager.getSystemAvailableFeatures();
  489 + for (FeatureInfo featureInfo : features) {
  490 + if (PackageManager.FEATURE_CAMERA_FLASH.equals(featureInfo.name)) { // 断设备是否支持闪光灯
  491 + try {
  492 + parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
  493 + isUse = true;
  494 + } catch (Exception e) {
  495 + e.printStackTrace();
  496 + }
  497 + }
  498 + }
  499 + if(!isUse) AlertUtil.showDeftToast(context,"该设备不支持闪光灯");
  500 + }
  501 + break;
  502 + case FLASH_MODE_OFF:
  503 + parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
  504 + break;
  505 + case ICameraControl.FLASH_MODE_AUTO:
  506 + parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
  507 + break;
  508 + default:
  509 + parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
  510 + break;
  511 + }
  512 + if (parameters == null || camera == null)
  513 + return;
  514 + camera.setParameters(parameters);
  515 + }
  516 +
  517 + private int getSurfaceOrientation() {
  518 + @CameraView.Orientation
  519 + int orientation = displayOrientation;
  520 + switch (orientation) {
  521 + case CameraView.ORIENTATION_PORTRAIT:
  522 + return 90;
  523 + case CameraView.ORIENTATION_HORIZONTAL:
  524 + return 0;
  525 + case CameraView.ORIENTATION_INVERT:
  526 + return 180;
  527 + default:
  528 + return 90;
  529 + }
  530 + }
  531 +
  532 + /**
  533 + * 有些相机匹配不到完美的比例。比如。我们的layout是4:3的。预览只有16:9
  534 + * 的,如果直接显示图片会拉伸,变形。缩放的话,又有黑边。所以我们采取的策略
  535 + * 是,等比例放大。这样预览的有一部分会超出屏幕。拍照后再进行裁剪处理。
  536 + */
  537 + private class PreviewView extends FrameLayout {
  538 +
  539 + private TextureView textureView;
  540 +
  541 + private float ratio = 0.75f;
  542 +
  543 + void setTextureView(TextureView textureView) {
  544 + this.textureView = textureView;
  545 + removeAllViews();
  546 + addView(textureView);
  547 + }
  548 +
  549 + void setRatio(float ratio) {
  550 + this.ratio = ratio;
  551 + requestLayout();
  552 + relayout(getWidth(), getHeight());
  553 + }
  554 +
  555 + public PreviewView(Context context) {
  556 + super(context);
  557 + }
  558 +
  559 + @Override
  560 + protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  561 + super.onSizeChanged(w, h, oldw, oldh);
  562 + relayout(w, h);
  563 + }
  564 +
  565 + private void relayout(int w, int h) {
  566 + int width = w;
  567 + int height = h;
  568 + if (w < h) {
  569 + // 垂直模式,高度固定。
  570 + height = (int) (width * ratio);
  571 + } else {
  572 + // 水平模式,宽度固定。
  573 + width = (int) (height * ratio);
  574 + }
  575 +
  576 + int l = (getWidth() - width) / 2;
  577 + int t = (getHeight() - height) / 2;
  578 +
  579 + previewFrame.left = l;
  580 + previewFrame.top = t;
  581 + previewFrame.right = l + width;
  582 + previewFrame.bottom = t + height;
  583 + }
  584 +
  585 + @Override
  586 + protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  587 + super.onLayout(changed, left, top, right, bottom);
  588 + textureView.layout(previewFrame.left, previewFrame.top, previewFrame.right, previewFrame.bottom);
  589 + }
  590 + }
  591 +
  592 + @Override
  593 + public Rect getPreviewFrame() {
  594 + return previewFrame;
  595 + }
  596 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/camera/Camera2Control.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +
  5 +package com.cnlive.core.ocr.ui.camera;
  6 +
  7 +import android.Manifest;
  8 +import android.annotation.SuppressLint;
  9 +import android.annotation.TargetApi;
  10 +import android.content.Context;
  11 +import android.content.pm.PackageManager;
  12 +import android.graphics.ImageFormat;
  13 +import android.graphics.Matrix;
  14 +import android.graphics.Point;
  15 +import android.graphics.Rect;
  16 +import android.graphics.RectF;
  17 +import android.graphics.SurfaceTexture;
  18 +import android.hardware.camera2.CameraAccessException;
  19 +import android.hardware.camera2.CameraCaptureSession;
  20 +import android.hardware.camera2.CameraCharacteristics;
  21 +import android.hardware.camera2.CameraDevice;
  22 +import android.hardware.camera2.CameraManager;
  23 +import android.hardware.camera2.CameraMetadata;
  24 +import android.hardware.camera2.CaptureFailure;
  25 +import android.hardware.camera2.CaptureRequest;
  26 +import android.hardware.camera2.CaptureResult;
  27 +import android.hardware.camera2.TotalCaptureResult;
  28 +import android.hardware.camera2.params.StreamConfigurationMap;
  29 +import android.media.Image;
  30 +import android.media.ImageReader;
  31 +import android.os.Build;
  32 +import android.os.Handler;
  33 +import android.os.HandlerThread;
  34 +import android.util.Size;
  35 +import android.util.SparseIntArray;
  36 +import android.view.Surface;
  37 +import android.view.TextureView;
  38 +import android.view.View;
  39 +import android.view.WindowManager;
  40 +
  41 +import java.nio.ByteBuffer;
  42 +import java.util.ArrayList;
  43 +import java.util.Arrays;
  44 +import java.util.Collections;
  45 +import java.util.Comparator;
  46 +import java.util.List;
  47 +import java.util.concurrent.Semaphore;
  48 +import java.util.concurrent.TimeUnit;
  49 +import java.util.concurrent.atomic.AtomicBoolean;
  50 +
  51 +import androidx.annotation.NonNull;
  52 +import androidx.core.content.ContextCompat;
  53 +
  54 +@TargetApi(Build.VERSION_CODES.LOLLIPOP)
  55 +public class Camera2Control implements ICameraControl {
  56 + @Override
  57 + public void setDetectCallback(OnDetectPictureCallback callback) {
  58 + // TODO 暂时只用camera
  59 + }
  60 +
  61 + @Override
  62 + public AtomicBoolean getAbortingScan() {
  63 + // TODO 暂时只用camera
  64 + return null;
  65 + }
  66 +
  67 + /**
  68 + * Conversion from screen rotation to JPEG orientation.
  69 + */
  70 + private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
  71 + private static final int MAX_PREVIEW_SIZE = 2048;
  72 +
  73 + static {
  74 + ORIENTATIONS.append(Surface.ROTATION_0, 90);
  75 + ORIENTATIONS.append(Surface.ROTATION_90, 0);
  76 + ORIENTATIONS.append(Surface.ROTATION_180, 270);
  77 + ORIENTATIONS.append(Surface.ROTATION_270, 180);
  78 + }
  79 +
  80 + private static final int STATE_PREVIEW = 0;
  81 + private static final int STATE_WAITING_FOR_LOCK = 1;
  82 + private static final int STATE_WAITING_FOR_CAPTURE = 2;
  83 + private static final int STATE_CAPTURING = 3;
  84 + private static final int STATE_PICTURE_TAKEN = 4;
  85 +
  86 + private static final int MAX_PREVIEW_WIDTH = 1920;
  87 + private static final int MAX_PREVIEW_HEIGHT = 1080;
  88 +
  89 + private int flashMode;
  90 + private int orientation = 0;
  91 + private int state = STATE_PREVIEW;
  92 +
  93 + private Context context;
  94 + private OnTakePictureCallback onTakePictureCallback;
  95 + private PermissionCallback permissionCallback;
  96 +
  97 + private String cameraId;
  98 + private TextureView textureView;
  99 + private Size previewSize;
  100 + private Rect previewFrame = new Rect();
  101 +
  102 + private HandlerThread backgroundThread;
  103 + private Handler backgroundHandler;
  104 + private ImageReader imageReader;
  105 + private CameraCaptureSession captureSession;
  106 + private CameraDevice cameraDevice;
  107 +
  108 + private CaptureRequest.Builder previewRequestBuilder;
  109 + private CaptureRequest previewRequest;
  110 +
  111 + private Semaphore cameraLock = new Semaphore(1);
  112 + private int sensorOrientation;
  113 +
  114 + @Override
  115 + public void start() {
  116 + startBackgroundThread();
  117 + if (textureView.isAvailable()) {
  118 + openCamera(textureView.getWidth(), textureView.getHeight());
  119 + textureView.setSurfaceTextureListener(surfaceTextureListener);
  120 + } else {
  121 + textureView.setSurfaceTextureListener(surfaceTextureListener);
  122 + }
  123 + }
  124 +
  125 + @Override
  126 + public void stop() {
  127 + textureView.setSurfaceTextureListener(null);
  128 + closeCamera();
  129 + stopBackgroundThread();
  130 + }
  131 +
  132 + @Override
  133 + public void pause() {
  134 + setFlashMode(FLASH_MODE_OFF);
  135 + }
  136 +
  137 + @Override
  138 + public void resume() {
  139 + state = STATE_PREVIEW;
  140 + }
  141 +
  142 + @Override
  143 + public View getDisplayView() {
  144 + return textureView;
  145 + }
  146 +
  147 + @Override
  148 + public Rect getPreviewFrame() {
  149 + return previewFrame;
  150 + }
  151 +
  152 + @Override
  153 + public void takePicture(OnTakePictureCallback callback) {
  154 + this.onTakePictureCallback = callback;
  155 + // 拍照第一步,对焦
  156 + lockFocus();
  157 + }
  158 +
  159 + @Override
  160 + public void setPermissionCallback(PermissionCallback callback) {
  161 + this.permissionCallback = callback;
  162 + }
  163 +
  164 + @Override
  165 + public void setDisplayOrientation(@CameraView.Orientation int displayOrientation) {
  166 + this.orientation = displayOrientation / 90;
  167 + }
  168 +
  169 + @Override
  170 + public void refreshPermission() {
  171 + openCamera(textureView.getWidth(), textureView.getHeight());
  172 + }
  173 +
  174 + @Override
  175 + public void setFlashMode(@FlashMode int flashMode) {
  176 + if (this.flashMode == flashMode) {
  177 + return;
  178 + }
  179 + this.flashMode = flashMode;
  180 + try {
  181 + previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
  182 + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
  183 + updateFlashMode(flashMode, previewRequestBuilder);
  184 + previewRequest = previewRequestBuilder.build();
  185 + captureSession.setRepeatingRequest(previewRequest, captureCallback, backgroundHandler);
  186 + } catch (Exception e) {
  187 + if (e!=null)
  188 + e.printStackTrace();
  189 + }
  190 + }
  191 +
  192 + @Override
  193 + public int getFlashMode() {
  194 + return flashMode;
  195 + }
  196 +
  197 + public Camera2Control(Context activity) {
  198 + this.context = activity;
  199 + textureView = new TextureView(activity);
  200 + }
  201 +
  202 + private final TextureView.SurfaceTextureListener surfaceTextureListener =
  203 + new TextureView.SurfaceTextureListener() {
  204 + @Override
  205 + public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
  206 + openCamera(width, height);
  207 + }
  208 +
  209 + @Override
  210 + public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
  211 + configureTransform(width, height);
  212 + previewFrame.left = 0;
  213 + previewFrame.top = 0;
  214 + previewFrame.right = width;
  215 + previewFrame.bottom = height;
  216 + }
  217 +
  218 + @Override
  219 + public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
  220 + stop();
  221 + return true;
  222 + }
  223 +
  224 + @Override
  225 + public void onSurfaceTextureUpdated(SurfaceTexture texture) {
  226 + }
  227 + };
  228 +
  229 + @SuppressLint("MissingPermission")
  230 + private void openCamera(int width, int height) {
  231 + // 6.0+的系统需要检查系统权限 。
  232 + if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
  233 + != PackageManager.PERMISSION_GRANTED) {
  234 + requestCameraPermission();
  235 + return;
  236 + }
  237 + setUpCameraOutputs(width, height);
  238 + configureTransform(width, height);
  239 + CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
  240 + try {
  241 + if (!cameraLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
  242 + throw new RuntimeException("Time out waiting to lock camera opening.");
  243 + }
  244 + manager.openCamera(cameraId, deviceStateCallback, backgroundHandler);
  245 + } catch (CameraAccessException e) {
  246 + if (e!=null)
  247 + e.printStackTrace();
  248 + } catch (InterruptedException e) {
  249 + if (e!=null)
  250 + throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
  251 + }
  252 + }
  253 +
  254 + private final CameraDevice.StateCallback deviceStateCallback = new CameraDevice.StateCallback() {
  255 + @Override
  256 + public void onOpened(@NonNull CameraDevice cameraDevice) {
  257 + cameraLock.release();
  258 + Camera2Control.this.cameraDevice = cameraDevice;
  259 + createCameraPreviewSession();
  260 + }
  261 +
  262 + @Override
  263 + public void onDisconnected(@NonNull CameraDevice cameraDevice) {
  264 + cameraLock.release();
  265 + cameraDevice.close();
  266 + Camera2Control.this.cameraDevice = null;
  267 + }
  268 +
  269 + @Override
  270 + public void onError(@NonNull CameraDevice cameraDevice, int error) {
  271 + cameraLock.release();
  272 + cameraDevice.close();
  273 + Camera2Control.this.cameraDevice = null;
  274 + }
  275 + };
  276 +
  277 + private void createCameraPreviewSession() {
  278 + try {
  279 + SurfaceTexture texture = textureView.getSurfaceTexture();
  280 + assert texture != null;
  281 + texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
  282 + Surface surface = new Surface(texture);
  283 + previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
  284 + previewRequestBuilder.addTarget(surface);
  285 + updateFlashMode(this.flashMode, previewRequestBuilder);
  286 + cameraDevice.createCaptureSession(Arrays.asList(surface, imageReader.getSurface()),
  287 + new CameraCaptureSession.StateCallback() {
  288 +
  289 + @Override
  290 + public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
  291 + // The camera is already closed
  292 + if (null == cameraDevice) {
  293 + return;
  294 + }
  295 + captureSession = cameraCaptureSession;
  296 + try {
  297 + previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
  298 + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
  299 + previewRequest = previewRequestBuilder.build();
  300 + captureSession.setRepeatingRequest(previewRequest,
  301 + captureCallback, backgroundHandler);
  302 + } catch (CameraAccessException e) {
  303 + if (e!=null)
  304 + e.printStackTrace();
  305 + }
  306 + }
  307 +
  308 + @Override
  309 + public void onConfigureFailed(
  310 + @NonNull CameraCaptureSession cameraCaptureSession) {
  311 + // TODO
  312 + }
  313 + }, null
  314 + );
  315 + } catch (CameraAccessException e) {
  316 + if (e!=null)
  317 + e.printStackTrace();
  318 + }
  319 + }
  320 +
  321 + private final ImageReader.OnImageAvailableListener onImageAvailableListener =
  322 + new ImageReader.OnImageAvailableListener() {
  323 + @Override
  324 + public void onImageAvailable(ImageReader reader) {
  325 + if (onTakePictureCallback != null) {
  326 + Image image = reader.acquireNextImage();
  327 + ByteBuffer buffer = image.getPlanes()[0].getBuffer();
  328 + byte[] bytes = new byte[buffer.remaining()];
  329 + buffer.get(bytes);
  330 +
  331 + image.close();
  332 + onTakePictureCallback.onPictureTaken(bytes);
  333 + }
  334 + }
  335 + };
  336 +
  337 + private CameraCaptureSession.CaptureCallback captureCallback =
  338 + new CameraCaptureSession.CaptureCallback() {
  339 + private void process(CaptureResult result) {
  340 + switch (state) {
  341 + case STATE_PREVIEW: {
  342 + break;
  343 + }
  344 + case STATE_WAITING_FOR_LOCK: {
  345 + Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
  346 + if (afState == null) {
  347 + captureStillPicture();
  348 + return;
  349 + }
  350 + switch (afState) {
  351 + case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED:
  352 + case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
  353 + case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED: {
  354 + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
  355 + if (aeState == null
  356 + || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
  357 + captureStillPicture();
  358 + } else {
  359 + runPreCaptureSequence();
  360 + }
  361 + break;
  362 + }
  363 + default:
  364 + captureStillPicture();
  365 + }
  366 + break;
  367 + }
  368 + case STATE_WAITING_FOR_CAPTURE: {
  369 + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
  370 + if (aeState == null
  371 + || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE
  372 + || aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
  373 + state = STATE_CAPTURING;
  374 + } else {
  375 + if (aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
  376 + captureStillPicture();
  377 + }
  378 + }
  379 + break;
  380 + }
  381 + case STATE_CAPTURING: {
  382 + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
  383 + if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
  384 + captureStillPicture();
  385 + }
  386 + break;
  387 + }
  388 + default:
  389 + break;
  390 + }
  391 + }
  392 +
  393 + @Override
  394 + public void onCaptureProgressed(@NonNull CameraCaptureSession session,
  395 + @NonNull CaptureRequest request,
  396 + @NonNull CaptureResult partialResult) {
  397 + process(partialResult);
  398 + }
  399 +
  400 + @Override
  401 + public void onCaptureCompleted(@NonNull CameraCaptureSession session,
  402 + @NonNull CaptureRequest request,
  403 + @NonNull TotalCaptureResult result) {
  404 + process(result);
  405 + }
  406 +
  407 + };
  408 +
  409 + private Size getOptimalSize(Size[] choices, int textureViewWidth,
  410 + int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
  411 + List<Size> bigEnough = new ArrayList<>();
  412 + List<Size> notBigEnough = new ArrayList<>();
  413 + int w = aspectRatio.getWidth();
  414 + int h = aspectRatio.getHeight();
  415 + for (Size option : choices) {
  416 + if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight
  417 + && option.getHeight() == option.getWidth() * h / w) {
  418 + if (option.getWidth() >= textureViewWidth
  419 + && option.getHeight() >= textureViewHeight) {
  420 + bigEnough.add(option);
  421 + } else {
  422 + notBigEnough.add(option);
  423 + }
  424 + }
  425 + }
  426 +
  427 + // Pick the smallest of those big enough. If there is no one big enough, pick the
  428 + // largest of those not big enough.
  429 + if (bigEnough.size() > 0) {
  430 + return Collections.min(bigEnough, sizeComparator);
  431 + }
  432 +
  433 + for (Size option : choices) {
  434 + if (option.getWidth() >= maxWidth && option.getHeight() >= maxHeight) {
  435 + return option;
  436 + }
  437 + }
  438 +
  439 + if (notBigEnough.size() > 0) {
  440 + return Collections.max(notBigEnough, sizeComparator);
  441 + }
  442 +
  443 + return choices[0];
  444 + }
  445 +
  446 + private Comparator<Size> sizeComparator = new Comparator<Size>() {
  447 + @Override
  448 + public int compare(Size lhs, Size rhs) {
  449 + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
  450 + }
  451 + };
  452 +
  453 + private void requestCameraPermission() {
  454 + if (permissionCallback != null) {
  455 + permissionCallback.onRequestPermission();
  456 + }
  457 + }
  458 +
  459 + @SuppressWarnings("SuspiciousNameCombination")
  460 + private void setUpCameraOutputs(int width, int height) {
  461 + CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
  462 + try {
  463 + for (String cameraId : manager.getCameraIdList()) {
  464 + CameraCharacteristics characteristics =
  465 + manager.getCameraCharacteristics(cameraId);
  466 +
  467 + Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
  468 + if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
  469 + continue;
  470 + }
  471 +
  472 + StreamConfigurationMap map = characteristics.get(
  473 + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
  474 + if (map == null) {
  475 + continue;
  476 + }
  477 +
  478 + // int preferredPictureSize = (int) (Math.max(textureView.getWidth(), textureView
  479 + // .getHeight()) * 1.5);
  480 + // preferredPictureSize = Math.min(preferredPictureSize, 2560);
  481 +
  482 + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  483 + Point screenSize = new Point();
  484 + windowManager.getDefaultDisplay().getSize(screenSize);
  485 + int maxImageSize = Math.max(MAX_PREVIEW_SIZE, screenSize.y * 3 / 2);
  486 +
  487 + Size size = getOptimalSize(map.getOutputSizes(ImageFormat.JPEG), textureView.getWidth(),
  488 + textureView.getHeight(), maxImageSize, maxImageSize, new Size(4, 3));
  489 +
  490 + imageReader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
  491 + ImageFormat.JPEG, 1);
  492 + imageReader.setOnImageAvailableListener(
  493 + onImageAvailableListener, backgroundHandler);
  494 +
  495 + int displayRotation = orientation;
  496 + // noinspection ConstantConditions
  497 + sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
  498 + boolean swappedDimensions = false;
  499 + switch (displayRotation) {
  500 + case Surface.ROTATION_0:
  501 + case Surface.ROTATION_180:
  502 + if (sensorOrientation == 90 || sensorOrientation == 270) {
  503 + swappedDimensions = true;
  504 + }
  505 + break;
  506 + case Surface.ROTATION_90:
  507 + case Surface.ROTATION_270:
  508 + if (sensorOrientation == 0 || sensorOrientation == 180) {
  509 + swappedDimensions = true;
  510 + }
  511 + break;
  512 + default:
  513 + }
  514 +
  515 + int rotatedPreviewWidth = width;
  516 + int rotatedPreviewHeight = height;
  517 + int maxPreviewWidth = screenSize.x;
  518 + int maxPreviewHeight = screenSize.y;
  519 +
  520 + if (swappedDimensions) {
  521 + rotatedPreviewWidth = height;
  522 + rotatedPreviewHeight = width;
  523 + maxPreviewWidth = screenSize.y;
  524 + maxPreviewHeight = screenSize.x;
  525 + }
  526 +
  527 + maxPreviewWidth = Math.min(maxPreviewWidth, MAX_PREVIEW_WIDTH);
  528 + maxPreviewHeight = Math.min(maxPreviewHeight, MAX_PREVIEW_HEIGHT);
  529 +
  530 + previewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class),
  531 + rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
  532 + maxPreviewHeight, size);
  533 + this.cameraId = cameraId;
  534 + return;
  535 + }
  536 + } catch (CameraAccessException | NullPointerException e) {
  537 + if (e!=null)
  538 + e.printStackTrace();
  539 + }
  540 + }
  541 +
  542 + private void closeCamera() {
  543 + try {
  544 + cameraLock.acquire();
  545 + if (null != captureSession) {
  546 + captureSession.close();
  547 + captureSession = null;
  548 + }
  549 + if (null != cameraDevice) {
  550 + cameraDevice.close();
  551 + cameraDevice = null;
  552 + }
  553 + if (null != imageReader) {
  554 + imageReader.close();
  555 + imageReader = null;
  556 + }
  557 + } catch (InterruptedException e) {
  558 + throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
  559 + } finally {
  560 + cameraLock.release();
  561 + }
  562 + }
  563 +
  564 + private void startBackgroundThread() {
  565 + backgroundThread = new HandlerThread("ocr_camera");
  566 + backgroundThread.start();
  567 + backgroundHandler = new Handler(backgroundThread.getLooper());
  568 + }
  569 +
  570 + private void stopBackgroundThread() {
  571 + if (backgroundThread != null) {
  572 + backgroundThread.quitSafely();
  573 + backgroundThread = null;
  574 + backgroundHandler = null;
  575 + }
  576 + }
  577 +
  578 + private void configureTransform(int viewWidth, int viewHeight) {
  579 + if (null == textureView || null == previewSize || null == context) {
  580 + return;
  581 + }
  582 + int rotation = orientation;
  583 + Matrix matrix = new Matrix();
  584 + RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
  585 + RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
  586 + float centerX = viewRect.centerX();
  587 + float centerY = viewRect.centerY();
  588 + if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
  589 + bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
  590 + matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
  591 + float scale = Math.max(
  592 + (float) viewHeight / previewSize.getHeight(),
  593 + (float) viewWidth / previewSize.getWidth());
  594 + matrix.postScale(scale, scale, centerX, centerY);
  595 + matrix.postRotate(90 * (rotation - 2), centerX, centerY);
  596 + } else if (Surface.ROTATION_180 == rotation) {
  597 + matrix.postRotate(180, centerX, centerY);
  598 + }
  599 + textureView.setTransform(matrix);
  600 + }
  601 +
  602 + // 拍照前,先对焦
  603 + private void lockFocus() {
  604 + if (captureSession != null && state == STATE_PREVIEW) {
  605 + try {
  606 + previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
  607 + CameraMetadata.CONTROL_AF_TRIGGER_START);
  608 + state = STATE_WAITING_FOR_LOCK;
  609 + captureSession.capture(previewRequestBuilder.build(), captureCallback,
  610 + backgroundHandler);
  611 + } catch (CameraAccessException e) {
  612 + if (e!=null)
  613 + e.printStackTrace();
  614 + }
  615 + }
  616 + }
  617 +
  618 + private void runPreCaptureSequence() {
  619 + try {
  620 + previewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
  621 + CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
  622 + state = STATE_WAITING_FOR_CAPTURE;
  623 + captureSession.capture(previewRequestBuilder.build(), captureCallback,
  624 + backgroundHandler);
  625 + } catch (CameraAccessException e) {
  626 + if (e!=null)
  627 + e.printStackTrace();
  628 + }
  629 + }
  630 +
  631 + // 拍照session
  632 + private void captureStillPicture() {
  633 + try {
  634 + if (null == context || null == cameraDevice) {
  635 + return;
  636 + }
  637 + final CaptureRequest.Builder captureBuilder =
  638 + cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
  639 + captureBuilder.addTarget(imageReader.getSurface());
  640 + captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
  641 + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
  642 +
  643 + captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(orientation));
  644 + updateFlashMode(this.flashMode, captureBuilder);
  645 + CameraCaptureSession.CaptureCallback captureCallback =
  646 + new CameraCaptureSession.CaptureCallback() {
  647 + @Override
  648 + public void onCaptureCompleted(@NonNull CameraCaptureSession session,
  649 + @NonNull CaptureRequest request,
  650 + @NonNull TotalCaptureResult result) {
  651 + unlockFocus();
  652 + }
  653 +
  654 + @Override
  655 + public void onCaptureFailed(@NonNull CameraCaptureSession session,
  656 + @NonNull CaptureRequest request,
  657 + @NonNull CaptureFailure failure) {
  658 + super.onCaptureFailed(session, request, failure);
  659 + }
  660 + };
  661 +
  662 + // 停止预览
  663 + captureSession.stopRepeating();
  664 + captureSession.capture(captureBuilder.build(), captureCallback, backgroundHandler);
  665 + state = STATE_PICTURE_TAKEN;
  666 + // unlockFocus();
  667 + } catch (CameraAccessException e) {
  668 + if (e!=null)
  669 + e.printStackTrace();
  670 + }
  671 + }
  672 +
  673 + private int getOrientation(int rotation) {
  674 + return (ORIENTATIONS.get(rotation) + sensorOrientation + 270) % 360;
  675 + }
  676 +
  677 + // 停止对焦
  678 + private void unlockFocus() {
  679 + try {
  680 + previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
  681 + CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
  682 + captureSession.capture(previewRequestBuilder.build(), captureCallback,
  683 + backgroundHandler);
  684 + state = STATE_PREVIEW;
  685 + // 预览
  686 + captureSession.setRepeatingRequest(previewRequest, captureCallback,
  687 + backgroundHandler);
  688 + textureView.setSurfaceTextureListener(surfaceTextureListener);
  689 + } catch (CameraAccessException e) {
  690 + if (e!=null)
  691 + e.printStackTrace();
  692 + }
  693 + }
  694 +
  695 + private void updateFlashMode(@FlashMode int flashMode, CaptureRequest.Builder builder) {
  696 + switch (flashMode) {
  697 + case FLASH_MODE_TORCH:
  698 + builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
  699 + break;
  700 + case FLASH_MODE_OFF:
  701 + builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);
  702 + break;
  703 + case ICameraControl.FLASH_MODE_AUTO:
  704 + default:
  705 + builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_SINGLE);
  706 + break;
  707 + }
  708 + }
  709 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/camera/CameraActivity.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.camera;
  5 +
  6 +import android.Manifest;
  7 +import android.app.Activity;
  8 +import android.content.Intent;
  9 +import android.content.pm.PackageManager;
  10 +import android.content.res.Configuration;
  11 +import android.graphics.Bitmap;
  12 +import android.graphics.Rect;
  13 +import android.graphics.drawable.BitmapDrawable;
  14 +import android.os.Build;
  15 +import android.os.Bundle;
  16 +import android.os.Environment;
  17 +import android.os.Handler;
  18 +import android.view.Surface;
  19 +import android.view.View;
  20 +import android.widget.ImageView;
  21 +import android.widget.Toast;
  22 +
  23 +import com.cnlive.core.libs.base.util.AlertUtil;
  24 +import com.cnlive.core.ocr.idcardquality.IDcardQualityProcess;
  25 +import com.cnlive.core.ocr.ui.crop.CropView;
  26 +import com.cnlive.core.ocr.ui.crop.FrameOverlayView;
  27 +import com.cnlive.core.people_verfication.R;
  28 +import com.cnlive.media.picker.ImagePicker;
  29 +import com.cnlive.media.picker.PickerConfig;
  30 +import com.cnlive.media.picker.entity.Media;
  31 +
  32 +import java.io.File;
  33 +import java.io.FileOutputStream;
  34 +import java.io.IOException;
  35 +import java.util.ArrayList;
  36 +
  37 +import androidx.annotation.NonNull;
  38 +import androidx.core.app.ActivityCompat;
  39 +
  40 +public class CameraActivity extends Activity {
  41 +
  42 + public static final String KEY_OUTPUT_FILE_PATH = "outputFilePath";
  43 + public static final String KEY_CONTENT_TYPE = "contentType";
  44 + public static final String KEY_NATIVE_TOKEN = "nativeToken";
  45 + public static final String KEY_NATIVE_ENABLE = "nativeEnable";
  46 +
  47 + public static final String CONTENT_TYPE_GENERAL = "general";
  48 + public static final String CONTENT_TYPE_ID_CARD_FRONT = "IDCardFront";
  49 + public static final String CONTENT_TYPE_ID_CARD_BACK = "IDCardBack";
  50 + public static final String CONTENT_TYPE_BANK_CARD = "bankCard";
  51 +
  52 + private static final int REQUEST_CODE_PICK_IMAGE = 100;
  53 + private static final int PERMISSIONS_REQUEST_CAMERA = 800;
  54 + private static final int PERMISSIONS_EXTERNAL_STORAGE = 801;
  55 +
  56 + private File outputFile;
  57 + private String contentType;
  58 + private Handler handler = new Handler();
  59 +
  60 + private OCRCameraLayout takePictureContainer;
  61 + private OCRCameraLayout cropContainer;
  62 + private OCRCameraLayout confirmResultContainer;
  63 + private ImageView lightButton;
  64 + private CameraView cameraView;
  65 + private ImageView displayImageView;
  66 + private CropView cropView;
  67 + private FrameOverlayView overlayView;
  68 + private MaskView cropMaskView;
  69 + private ImageView takePhotoBtn;
  70 + private ImageView back;
  71 + //上一个被点击过的btn的点击时间
  72 + private long preViewClickTime = 0;
  73 + private PermissionCallback permissionCallback = new PermissionCallback() {
  74 + @Override
  75 + public boolean onRequestPermission() {
  76 + ActivityCompat.requestPermissions(CameraActivity.this,
  77 + new String[]{Manifest.permission.CAMERA,
  78 + Manifest.permission.WRITE_EXTERNAL_STORAGE,
  79 + Manifest.permission.READ_EXTERNAL_STORAGE},
  80 + PERMISSIONS_REQUEST_CAMERA);
  81 + return false;
  82 + }
  83 + };
  84 +
  85 + @Override
  86 + protected void onCreate(Bundle savedInstanceState) {
  87 +
  88 + super.onCreate(savedInstanceState);
  89 + setContentView(R.layout.bd_ocr_activity_camera);
  90 +
  91 + takePictureContainer = (OCRCameraLayout) findViewById(R.id.take_picture_container);
  92 + confirmResultContainer = (OCRCameraLayout) findViewById(R.id.confirm_result_container);
  93 +
  94 + cameraView = (CameraView) findViewById(R.id.camera_view);
  95 + cameraView.getCameraControl().setPermissionCallback(permissionCallback);
  96 + lightButton = (ImageView) findViewById(R.id.light_button);
  97 + lightButton.setOnClickListener(lightButtonOnClickListener);
  98 + takePhotoBtn = (ImageView) findViewById(R.id.take_photo_button);
  99 + findViewById(R.id.album_button).setOnClickListener(albumButtonOnClickListener);
  100 + takePhotoBtn.setOnClickListener(takeButtonOnClickListener);
  101 +
  102 + // confirm result;
  103 + displayImageView = (ImageView) findViewById(R.id.display_image_view);
  104 + confirmResultContainer.findViewById(R.id.confirm_button).setOnClickListener(confirmButtonOnClickListener);
  105 + confirmResultContainer.findViewById(R.id.cancel_button).setOnClickListener(confirmCancelButtonOnClickListener);
  106 + findViewById(R.id.rotate_button).setOnClickListener(rotateButtonOnClickListener);
  107 +
  108 + cropView = (CropView) findViewById(R.id.crop_view);
  109 + cropContainer = (OCRCameraLayout) findViewById(R.id.crop_container);
  110 + overlayView = (FrameOverlayView) findViewById(R.id.overlay_view);
  111 + cropContainer.findViewById(R.id.confirm_button).setOnClickListener(cropConfirmButtonListener);
  112 + cropMaskView = (MaskView) cropContainer.findViewById(R.id.crop_mask_view);
  113 + cropContainer.findViewById(R.id.cancel_button).setOnClickListener(cropCancelButtonListener);
  114 + back = findViewById(R.id.back);
  115 + back.setOnClickListener(finishListener);
  116 + setOrientation(getResources().getConfiguration());
  117 + initParams();
  118 +
  119 + cameraView.setAutoPictureCallback(autoTakePictureCallback);
  120 + }
  121 +
  122 + @Override
  123 + protected void onStart() {
  124 + super.onStart();
  125 + cameraView.start();
  126 + }
  127 +
  128 + @Override
  129 + protected void onStop() {
  130 + super.onStop();
  131 + cameraView.stop();
  132 + }
  133 +
  134 + private void initParams() {
  135 + String outputPath = getIntent().getStringExtra(KEY_OUTPUT_FILE_PATH);
  136 + final String token = getIntent().getStringExtra(KEY_NATIVE_TOKEN);
  137 + boolean isNativeEnable = getIntent().getBooleanExtra(KEY_NATIVE_ENABLE, true);
  138 + if (token == null) {
  139 + isNativeEnable = false;
  140 + }
  141 +
  142 + if (outputPath != null) {
  143 + outputFile = new File(outputPath);
  144 + }
  145 +
  146 + contentType = getIntent().getStringExtra(KEY_CONTENT_TYPE);
  147 + if (contentType == null) {
  148 + contentType = CONTENT_TYPE_GENERAL;
  149 + }
  150 + int maskType;
  151 + switch (contentType) {
  152 + case CONTENT_TYPE_ID_CARD_FRONT:
  153 + maskType = MaskView.MASK_TYPE_ID_CARD_FRONT;
  154 + overlayView.setVisibility(View.INVISIBLE);
  155 + if (isNativeEnable) {
  156 + takePhotoBtn.setVisibility(View.INVISIBLE);
  157 + }
  158 + break;
  159 + case CONTENT_TYPE_ID_CARD_BACK:
  160 + maskType = MaskView.MASK_TYPE_ID_CARD_BACK;
  161 + overlayView.setVisibility(View.INVISIBLE);
  162 + if (isNativeEnable) {
  163 + takePhotoBtn.setVisibility(View.INVISIBLE);
  164 + }
  165 + break;
  166 + case CONTENT_TYPE_BANK_CARD:
  167 + maskType = MaskView.MASK_TYPE_BANK_CARD;
  168 + overlayView.setVisibility(View.INVISIBLE);
  169 + break;
  170 + case CONTENT_TYPE_GENERAL:
  171 + default:
  172 + maskType = MaskView.MASK_TYPE_NONE;
  173 + cropMaskView.setVisibility(View.INVISIBLE);
  174 + break;
  175 + }
  176 +
  177 + // 身份证本地能力初始化
  178 + if (maskType == MaskView.MASK_TYPE_ID_CARD_FRONT || maskType == MaskView.MASK_TYPE_ID_CARD_BACK) {
  179 + if (isNativeEnable) {
  180 + initNative(token);
  181 + }
  182 + }
  183 + cameraView.setEnableScan(isNativeEnable);
  184 + cameraView.setMaskType(maskType, this);
  185 + cropMaskView.setMaskType(maskType);
  186 + }
  187 +
  188 + private void initNative(final String token) {
  189 + CameraThreadPool.execute(new Runnable() {
  190 + @Override
  191 + public void run() {
  192 + // 加载本地so失败, 异常返回getloadSoException
  193 + if (IDcardQualityProcess.getLoadSoException() != null) {
  194 + cameraView.setInitNativeStatus(CameraView.NATIVE_SOLOAD_FAIL);
  195 + return;
  196 + }
  197 + // 授权状态
  198 + int authStatus = IDcardQualityProcess.init(token);
  199 + // 加载模型状态
  200 + int initModelStatus = IDcardQualityProcess.getInstance()
  201 + .idcardQualityInit(CameraActivity.this.getAssets(),
  202 + "models");
  203 + if (authStatus != 0) {
  204 + cameraView.setInitNativeStatus(CameraView.NATIVE_AUTH_FAIL);
  205 + return;
  206 + }
  207 + if (initModelStatus != 0) {
  208 + cameraView.setInitNativeStatus(CameraView.NATIVE_INIT_FAIL);
  209 + return;
  210 + }
  211 + }
  212 + });
  213 + }
  214 +
  215 + private void showTakePicture() {
  216 + cameraView.getCameraControl().resume();
  217 + updateFlashMode();
  218 + takePictureContainer.setVisibility(View.VISIBLE);
  219 + back.setVisibility(View.VISIBLE);
  220 + confirmResultContainer.setVisibility(View.INVISIBLE);
  221 + cropContainer.setVisibility(View.INVISIBLE);
  222 + }
  223 +
  224 + private void showCrop() {
  225 + cameraView.getCameraControl().pause();
  226 + updateFlashMode();
  227 + takePictureContainer.setVisibility(View.INVISIBLE);
  228 + back.setVisibility(View.INVISIBLE);
  229 + confirmResultContainer.setVisibility(View.INVISIBLE);
  230 + cropContainer.setVisibility(View.VISIBLE);
  231 + }
  232 +
  233 + private void showResultConfirm() {
  234 + cameraView.getCameraControl().pause();
  235 + updateFlashMode();
  236 + takePictureContainer.setVisibility(View.INVISIBLE);
  237 + back.setVisibility(View.INVISIBLE);
  238 + confirmResultContainer.setVisibility(View.VISIBLE);
  239 + cropContainer.setVisibility(View.INVISIBLE);
  240 + }
  241 +
  242 + // take photo;
  243 + private void updateFlashMode() {
  244 + int flashMode = cameraView.getCameraControl().getFlashMode();
  245 + if (flashMode == ICameraControl.FLASH_MODE_TORCH) {
  246 + lightButton.setImageResource(R.drawable.bd_ocr_light_on);
  247 + } else {
  248 + lightButton.setImageResource(R.drawable.bd_ocr_light_off);
  249 + }
  250 + }
  251 +
  252 + private View.OnClickListener albumButtonOnClickListener = new View.OnClickListener() {
  253 + @Override
  254 + public void onClick(View v) {
  255 + long curClickTime = System.currentTimeMillis();
  256 + if (preViewClickTime > 0 && curClickTime - preViewClickTime <= 2000 ) {
  257 + return;
  258 + }
  259 + preViewClickTime = curClickTime;
  260 + if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
  261 + != PackageManager.PERMISSION_GRANTED) {
  262 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
  263 + ActivityCompat.requestPermissions(CameraActivity.this,
  264 + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
  265 + PERMISSIONS_EXTERNAL_STORAGE);
  266 + return;
  267 + }
  268 + }
  269 + new ImagePicker
  270 + .Builder()
  271 + .setSelectGif(false)
  272 + .maxImageSize(20 * 1024 * 1024)
  273 + //最大选择图片数目,默认40张
  274 + .maxNum(1)
  275 + //选择模式(默认PICKER_IMAGE_VIDEO) PICKER_IMAGE-->图片 PICKER_VIDEO-->视频 PICKER_IMAGE_VIDEO-->图片&视频
  276 + .selectMode(PickerConfig.PICKER_IMAGE)
  277 + //拍照或录像存储位置 默认Environment.getExternalStorageDirectory().getPath() + File.separator + "JCamera"
  278 + .cachePath(Environment.getExternalStorageDirectory() + "/strike/file/")
  279 + //是否为发朋友圈模式(默认false) true-->图片与视频不能同时选择
  280 + .isFriendCircle(true)
  281 + //裁剪 只有在单选模式下才可以使用
  282 +// .doCrop(1, 1, 300, 300)
  283 + //是否开启单选模式(默认false)
  284 + .isSinglePick(true)
  285 + .builder()
  286 + .start(CameraActivity.this, PickerConfig.PICKER_IMAGE, REQUEST_CODE_PICK_IMAGE);
  287 + }
  288 + };
  289 +
  290 + private View.OnClickListener lightButtonOnClickListener = new View.OnClickListener() {
  291 + @Override
  292 + public void onClick(View v) {
  293 + if (cameraView.getCameraControl().getFlashMode() == ICameraControl.FLASH_MODE_OFF) {
  294 + cameraView.getCameraControl().setFlashMode(ICameraControl.FLASH_MODE_TORCH);
  295 + } else {
  296 + cameraView.getCameraControl().setFlashMode(ICameraControl.FLASH_MODE_OFF);
  297 + }
  298 + updateFlashMode();
  299 + }
  300 + };
  301 +
  302 + private View.OnClickListener takeButtonOnClickListener = new View.OnClickListener() {
  303 + @Override
  304 + public void onClick(View v) {
  305 + if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA)
  306 + != PackageManager.PERMISSION_GRANTED) {
  307 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
  308 + ActivityCompat.requestPermissions(CameraActivity.this,
  309 + new String[]{Manifest.permission.CAMERA,
  310 + Manifest.permission.WRITE_EXTERNAL_STORAGE,
  311 + Manifest.permission.READ_EXTERNAL_STORAGE},
  312 + PERMISSIONS_REQUEST_CAMERA);
  313 + return;
  314 + }
  315 + }
  316 + cameraView.takePicture(outputFile, takePictureCallback);
  317 + }
  318 + };
  319 +
  320 + private CameraView.OnTakePictureCallback autoTakePictureCallback = new CameraView.OnTakePictureCallback() {
  321 + @Override
  322 + public void onPictureTaken(final Bitmap bitmap) {
  323 + CameraThreadPool.execute(new Runnable() {
  324 + @Override
  325 + public void run() {
  326 + try {
  327 + FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
  328 + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
  329 + bitmap.recycle();
  330 + fileOutputStream.close();
  331 + } catch (IOException e) {
  332 + e.printStackTrace();
  333 + }
  334 + Intent intent = new Intent();
  335 + intent.putExtra(CameraActivity.KEY_CONTENT_TYPE, contentType);
  336 + setResult(Activity.RESULT_OK, intent);
  337 + finish();
  338 + }
  339 + });
  340 + }
  341 + };
  342 +
  343 + private CameraView.OnTakePictureCallback takePictureCallback = new CameraView.OnTakePictureCallback() {
  344 + @Override
  345 + public void onPictureTaken(final Bitmap bitmap) {
  346 + handler.post(new Runnable() {
  347 + @Override
  348 + public void run() {
  349 + takePictureContainer.setVisibility(View.INVISIBLE);
  350 + back.setVisibility(View.INVISIBLE);
  351 + if (cropMaskView.getMaskType() == MaskView.MASK_TYPE_NONE) {
  352 + cropView.setFilePath(outputFile.getAbsolutePath());
  353 + showCrop();
  354 + } else if (cropMaskView.getMaskType() == MaskView.MASK_TYPE_BANK_CARD) {
  355 + cropView.setFilePath(outputFile.getAbsolutePath());
  356 + cropMaskView.setVisibility(View.INVISIBLE);
  357 + overlayView.setVisibility(View.VISIBLE);
  358 + overlayView.setTypeWide();
  359 + showCrop();
  360 + } else {
  361 + displayImageView.setImageBitmap(bitmap);
  362 + showResultConfirm();
  363 + }
  364 + }
  365 + });
  366 + }
  367 + };
  368 +
  369 + private View.OnClickListener cropCancelButtonListener = new View.OnClickListener() {
  370 + @Override
  371 + public void onClick(View v) {
  372 + // 释放 cropView中的bitmap;
  373 + cropView.setFilePath(null);
  374 + showTakePicture();
  375 + }
  376 + };
  377 +
  378 +
  379 + private View.OnClickListener finishListener = new View.OnClickListener() {
  380 + @Override
  381 + public void onClick(View v) {
  382 + finish();
  383 + }
  384 + };
  385 +
  386 + private View.OnClickListener cropConfirmButtonListener = new View.OnClickListener() {
  387 + @Override
  388 + public void onClick(View v) {
  389 + int maskType = cropMaskView.getMaskType();
  390 + Rect rect;
  391 + switch (maskType) {
  392 + case MaskView.MASK_TYPE_BANK_CARD:
  393 + case MaskView.MASK_TYPE_ID_CARD_BACK:
  394 + case MaskView.MASK_TYPE_ID_CARD_FRONT:
  395 + rect = cropMaskView.getFrameRect();
  396 + break;
  397 + case MaskView.MASK_TYPE_NONE:
  398 + default:
  399 + rect = overlayView.getFrameRect();
  400 + break;
  401 + }
  402 + Bitmap cropped = cropView.crop(rect);
  403 + if(cropped == null){
  404 + return;
  405 + }
  406 + displayImageView.setImageBitmap(cropped);
  407 + cropAndConfirm();
  408 + }
  409 + };
  410 +
  411 + private void cropAndConfirm() {
  412 + cameraView.getCameraControl().pause();
  413 + updateFlashMode();
  414 + doConfirmResult();
  415 + }
  416 +
  417 + private void doConfirmResult() {
  418 + CameraThreadPool.execute(new Runnable() {
  419 + @Override
  420 + public void run() {
  421 + try {
  422 + FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
  423 + Bitmap bitmap = ((BitmapDrawable) displayImageView.getDrawable()).getBitmap();
  424 + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
  425 + fileOutputStream.close();
  426 + } catch (IOException e) {
  427 + if (e!=null)
  428 + e.printStackTrace();
  429 + }
  430 + Intent intent = new Intent();
  431 + intent.putExtra(CameraActivity.KEY_CONTENT_TYPE, contentType);
  432 + setResult(Activity.RESULT_OK, intent);
  433 + finish();
  434 + }
  435 + });
  436 + }
  437 +
  438 + private View.OnClickListener confirmButtonOnClickListener = new View.OnClickListener() {
  439 + @Override
  440 + public void onClick(View v) {
  441 + doConfirmResult();
  442 + }
  443 + };
  444 +
  445 + private View.OnClickListener confirmCancelButtonOnClickListener = new View.OnClickListener() {
  446 + @Override
  447 + public void onClick(View v) {
  448 + displayImageView.setImageBitmap(null);
  449 + showTakePicture();
  450 + }
  451 + };
  452 +
  453 + private View.OnClickListener rotateButtonOnClickListener = new View.OnClickListener() {
  454 + @Override
  455 + public void onClick(View v) {
  456 + cropView.rotate(90);
  457 + }
  458 + };
  459 +
  460 + @Override
  461 + public void onConfigurationChanged(Configuration newConfig) {
  462 + super.onConfigurationChanged(newConfig);
  463 + setOrientation(newConfig);
  464 + }
  465 +
  466 + private void setOrientation(Configuration newConfig) {
  467 + int rotation = getWindowManager().getDefaultDisplay().getRotation();
  468 + int orientation;
  469 + int cameraViewOrientation = CameraView.ORIENTATION_PORTRAIT;
  470 + switch (newConfig.orientation) {
  471 + case Configuration.ORIENTATION_PORTRAIT:
  472 + cameraViewOrientation = CameraView.ORIENTATION_PORTRAIT;
  473 + orientation = OCRCameraLayout.ORIENTATION_PORTRAIT;
  474 + break;
  475 + case Configuration.ORIENTATION_LANDSCAPE:
  476 + orientation = OCRCameraLayout.ORIENTATION_HORIZONTAL;
  477 + if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {
  478 + cameraViewOrientation = CameraView.ORIENTATION_HORIZONTAL;
  479 + } else {
  480 + cameraViewOrientation = CameraView.ORIENTATION_INVERT;
  481 + }
  482 + break;
  483 + default:
  484 + orientation = OCRCameraLayout.ORIENTATION_PORTRAIT;
  485 + cameraView.setOrientation(CameraView.ORIENTATION_PORTRAIT);
  486 + break;
  487 + }
  488 + takePictureContainer.setOrientation(orientation);
  489 + cameraView.setOrientation(cameraViewOrientation);
  490 + cropContainer.setOrientation(orientation);
  491 + confirmResultContainer.setOrientation(orientation);
  492 + }
  493 +
  494 + @Override
  495 + protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  496 + super.onActivityResult(requestCode, resultCode, data);
  497 + if (requestCode == REQUEST_CODE_PICK_IMAGE && data != null) {
  498 + ArrayList<Media> select = data.getParcelableArrayListExtra(PickerConfig.EXTRA_RESULT);
  499 + String path = select.get(0).path;
  500 + cropView.setFilePath(path);
  501 + showCrop();
  502 + }else {
  503 + cameraView.getCameraControl().resume();
  504 + }
  505 + }
  506 +
  507 +
  508 +
  509 + @Override
  510 + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
  511 + @NonNull int[] grantResults) {
  512 + super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  513 + switch (requestCode) {
  514 + case PERMISSIONS_REQUEST_CAMERA: {
  515 + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  516 + cameraView.getCameraControl().refreshPermission();
  517 + } else {
  518 + AlertUtil.showLongToast(getApplicationContext(), getString(R.string.camera_permission_required), Toast.LENGTH_LONG);
  519 + }
  520 + break;
  521 + }
  522 + case PERMISSIONS_EXTERNAL_STORAGE:
  523 + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  524 +
  525 + } else {
  526 + AlertUtil.showLongToast(getApplicationContext(), getString(R.string.permission_required), Toast.LENGTH_LONG);
  527 + }
  528 + default:
  529 + break;
  530 + }
  531 + }
  532 +
  533 + /**
  534 + * 做一些收尾工作,把这些工作从onDestory中移到正确的位置
  535 + * 可以以防止新的activity启动后被调用
  536 + */
  537 + private void doClear() {
  538 + CameraThreadPool.cancelAutoFocusTimer();
  539 + IDcardQualityProcess.getInstance().releaseModel();
  540 + }
  541 +
  542 +
  543 + @Override
  544 + protected void onDestroy() {
  545 + super.onDestroy();
  546 + this.doClear();
  547 + }
  548 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/camera/CameraThreadPool.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.camera;
  5 +
  6 +import java.util.Timer;
  7 +import java.util.TimerTask;
  8 +import java.util.concurrent.ExecutorService;
  9 +import java.util.concurrent.Executors;
  10 +
  11 +public class CameraThreadPool {
  12 +
  13 + static Timer timerFocus = null;
  14 +
  15 + /*
  16 + * 对焦频率
  17 + */
  18 + static final long cameraScanInterval = 2000;
  19 +
  20 + /*
  21 + * 线程池大小
  22 + */
  23 + private static int poolCount = Runtime.getRuntime().availableProcessors();
  24 +
  25 + private static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(poolCount);
  26 +
  27 + /**
  28 + * 给线程池添加任务
  29 + *
  30 + * @param runnable 任务
  31 + */
  32 + public static void execute(Runnable runnable) {
  33 + fixedThreadPool.execute(runnable);
  34 + }
  35 +
  36 + /**
  37 + * 创建一个定时对焦的timer任务
  38 + *
  39 + * @param runnable 对焦代码
  40 + * @return Timer Timer对象,用来终止自动对焦
  41 + */
  42 + public static Timer createAutoFocusTimerTask(final Runnable runnable) {
  43 + if (timerFocus != null) {
  44 + return timerFocus;
  45 + }
  46 + timerFocus = new Timer();
  47 + TimerTask task = new TimerTask() {
  48 + @Override
  49 + public void run() {
  50 + runnable.run();
  51 + }
  52 + };
  53 + timerFocus.scheduleAtFixedRate(task, 0, cameraScanInterval);
  54 + return timerFocus;
  55 + }
  56 +
  57 + /**
  58 + * 终止自动对焦任务,实际调用了cancel方法并且清空对象
  59 + * 但是无法终止执行中的任务,需额外处理
  60 + */
  61 + public static void cancelAutoFocusTimer() {
  62 + if (timerFocus != null) {
  63 + timerFocus.cancel();
  64 + timerFocus = null;
  65 + }
  66 + }
  67 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/camera/CameraView.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.camera;
  5 +
  6 +import android.content.Context;
  7 +import android.graphics.Bitmap;
  8 +import android.graphics.BitmapFactory;
  9 +import android.graphics.BitmapRegionDecoder;
  10 +import android.graphics.Color;
  11 +import android.graphics.Matrix;
  12 +import android.graphics.Rect;
  13 +import android.os.Handler;
  14 +import android.os.Looper;
  15 +import android.util.AttributeSet;
  16 +import android.util.TypedValue;
  17 +import android.view.Gravity;
  18 +import android.view.View;
  19 +import android.widget.FrameLayout;
  20 +import android.widget.ImageView;
  21 +import android.widget.LinearLayout;
  22 +import android.widget.TextView;
  23 +
  24 +import com.cnlive.core.ocr.idcardquality.IDcardQualityProcess;
  25 +import com.cnlive.core.ocr.ui.util.ImageUtil;
  26 +import com.cnlive.core.ocr.ui.util.PxUtil;
  27 +import com.cnlive.core.people_verfication.R;
  28 +
  29 +import java.io.File;
  30 +import java.io.FileOutputStream;
  31 +import java.io.IOException;
  32 +
  33 +import androidx.annotation.IntDef;
  34 +
  35 +/**
  36 + * 负责,相机的管理。同时提供,裁剪遮罩功能。
  37 + */
  38 +public class CameraView extends FrameLayout {
  39 +
  40 + private int maskType;
  41 +
  42 + /**
  43 + * 照相回调
  44 + */
  45 + interface OnTakePictureCallback {
  46 + void onPictureTaken(Bitmap bitmap);
  47 + }
  48 +
  49 + /**
  50 + * 垂直方向 {@link #setOrientation(int)}
  51 + */
  52 + public static final int ORIENTATION_PORTRAIT = 0;
  53 + /**
  54 + * 水平方向 {@link #setOrientation(int)}
  55 + */
  56 + public static final int ORIENTATION_HORIZONTAL = 90;
  57 + /**
  58 + * 水平翻转方向 {@link #setOrientation(int)}
  59 + */
  60 + public static final int ORIENTATION_INVERT = 270;
  61 +
  62 + /**
  63 + * 本地模型授权,加载成功
  64 + */
  65 + public static final int NATIVE_AUTH_INIT_SUCCESS = 0;
  66 +
  67 + /**
  68 + * 本地模型授权,加载成功
  69 + */
  70 + public static final int NATIVE_SOLOAD_FAIL = 10;
  71 +
  72 + /**
  73 + * 本地模型授权,加载成功
  74 + */
  75 + public static final int NATIVE_AUTH_FAIL = 11;
  76 +
  77 + /**
  78 + * 本地模型授权,加载成功
  79 + */
  80 + public static final int NATIVE_INIT_FAIL = 12;
  81 +
  82 +
  83 + /**
  84 + * 是否已经通过本地质量控制扫描
  85 + */
  86 + private final int SCAN_SUCCESS = 0;
  87 +
  88 + public void setInitNativeStatus(int initNativeStatus) {
  89 + this.initNativeStatus = initNativeStatus;
  90 + }
  91 +
  92 + /**
  93 + * 本地检测初始化,模型加载标识
  94 + */
  95 + private int initNativeStatus = NATIVE_AUTH_INIT_SUCCESS;
  96 +
  97 + @IntDef({ORIENTATION_PORTRAIT, ORIENTATION_HORIZONTAL, ORIENTATION_INVERT})
  98 + public @interface Orientation {
  99 +
  100 + }
  101 +
  102 + private CameraViewTakePictureCallback cameraViewTakePictureCallback = new CameraViewTakePictureCallback();
  103 +
  104 + private ICameraControl cameraControl;
  105 +
  106 + /**
  107 + * 相机预览View
  108 + */
  109 + private View displayView;
  110 + /**
  111 + * 身份证,银行卡,等裁剪用的遮罩
  112 + */
  113 + private MaskView maskView;
  114 +
  115 + /**
  116 + * 用于显示提示证 "请对齐身份证正面" 之类的背景
  117 + */
  118 + private ImageView hintView;
  119 +
  120 + /**
  121 + * 用于显示提示证 "请对齐身份证正面" 之类的文字
  122 + */
  123 + private TextView hintViewText;
  124 +
  125 + /**
  126 + * 提示文案容器
  127 + */
  128 + private LinearLayout hintViewTextWrapper;
  129 +
  130 + /**
  131 + * 是否是本地质量控制扫描
  132 + */
  133 + private boolean isEnableScan;
  134 +
  135 + public void setEnableScan(boolean enableScan) {
  136 + isEnableScan = enableScan;
  137 + }
  138 +
  139 + /**
  140 + * UI线程的handler
  141 + */
  142 + Handler uiHandler = new Handler(Looper.getMainLooper());
  143 +
  144 + public ICameraControl getCameraControl() {
  145 + return cameraControl;
  146 + }
  147 +
  148 + public void setOrientation(@Orientation int orientation) {
  149 + cameraControl.setDisplayOrientation(orientation);
  150 + }
  151 +
  152 + public CameraView(Context context) {
  153 + super(context);
  154 + init();
  155 + }
  156 +
  157 + public CameraView(Context context, AttributeSet attrs) {
  158 + super(context, attrs);
  159 + init();
  160 + }
  161 +
  162 + public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
  163 + super(context, attrs, defStyleAttr);
  164 + init();
  165 + }
  166 +
  167 + public void start() {
  168 + cameraControl.start();
  169 + setKeepScreenOn(true);
  170 + }
  171 +
  172 + public void stop() {
  173 + cameraControl.stop();
  174 + setKeepScreenOn(false);
  175 + }
  176 +
  177 + public void takePicture(final File file, final OnTakePictureCallback callback) {
  178 + cameraViewTakePictureCallback.file = file;
  179 + cameraViewTakePictureCallback.callback = callback;
  180 + cameraControl.takePicture(cameraViewTakePictureCallback);
  181 + }
  182 +
  183 + private OnTakePictureCallback autoPictureCallback;
  184 +
  185 + public void setAutoPictureCallback(OnTakePictureCallback callback) {
  186 + autoPictureCallback = callback;
  187 + }
  188 +
  189 + public void setMaskType(@MaskView.MaskType int maskType, final Context ctx) {
  190 + maskView.setMaskType(maskType);
  191 +
  192 + maskView.setVisibility(VISIBLE);
  193 + hintView.setVisibility(VISIBLE);
  194 +
  195 + int hintResourceId = R.drawable.bd_ocr_hint_align_id_card;
  196 + this.maskType = maskType;
  197 + boolean isNeedSetImage = true;
  198 + switch (maskType) {
  199 + case MaskView.MASK_TYPE_ID_CARD_FRONT:
  200 + hintResourceId = R.drawable.bd_ocr_round_corner;
  201 + isNeedSetImage = false;
  202 + break;
  203 + case MaskView.MASK_TYPE_ID_CARD_BACK:
  204 + isNeedSetImage = false;
  205 + hintResourceId = R.drawable.bd_ocr_round_corner;
  206 + break;
  207 + case MaskView.MASK_TYPE_BANK_CARD:
  208 + hintResourceId = R.drawable.bd_ocr_hint_align_bank_card;
  209 + break;
  210 + case MaskView.MASK_TYPE_NONE:
  211 + default:
  212 + maskView.setVisibility(INVISIBLE);
  213 + hintView.setVisibility(INVISIBLE);
  214 + break;
  215 + }
  216 +
  217 + if (isNeedSetImage) {
  218 + hintView.setImageResource(hintResourceId);
  219 + hintViewTextWrapper.setVisibility(INVISIBLE);
  220 + }
  221 +
  222 + if (maskType == MaskView.MASK_TYPE_ID_CARD_FRONT && isEnableScan) {
  223 + cameraControl.setDetectCallback(new ICameraControl.OnDetectPictureCallback() {
  224 + @Override
  225 + public int onDetect(byte[] data, int rotation) {
  226 + return detect(data, rotation);
  227 + }
  228 + });
  229 + }
  230 +
  231 + if (maskType == MaskView.MASK_TYPE_ID_CARD_BACK && isEnableScan) {
  232 + cameraControl.setDetectCallback(new ICameraControl.OnDetectPictureCallback() {
  233 + @Override
  234 + public int onDetect(byte[] data, int rotation) {
  235 + return detect(data, rotation);
  236 + }
  237 + });
  238 + }
  239 + }
  240 +
  241 + private int detect(byte[] data, final int rotation) {
  242 + if (initNativeStatus != NATIVE_AUTH_INIT_SUCCESS) {
  243 + showTipMessage(initNativeStatus);
  244 + return 1;
  245 + }
  246 + // 扫描成功阻止多余的操作
  247 + if (cameraControl.getAbortingScan().get()) {
  248 + return 0;
  249 + }
  250 + // BitmapRegionDecoder不会将整个图片加载到内存。
  251 + BitmapRegionDecoder decoder = null;
  252 + try {
  253 + decoder = BitmapRegionDecoder.newInstance(data, 0, data.length, true);
  254 + } catch (IOException e) {
  255 + if (e!=null)
  256 + e.printStackTrace();
  257 + }
  258 +
  259 + Rect previewFrame = cameraControl.getPreviewFrame();
  260 +
  261 + int width = rotation % 180 == 0 ? decoder.getWidth() : decoder.getHeight();
  262 + int height = rotation % 180 == 0 ? decoder.getHeight() : decoder.getWidth();
  263 +
  264 + Rect frameRect = maskView.getFrameRectExtend();
  265 +
  266 + int left = width * frameRect.left / maskView.getWidth();
  267 + int top = height * frameRect.top / maskView.getHeight();
  268 + int right = width * frameRect.right / maskView.getWidth();
  269 + int bottom = height * frameRect.bottom / maskView.getHeight();
  270 +
  271 + // 高度大于图片
  272 + if (previewFrame.top < 0) {
  273 + // 宽度对齐。
  274 + int adjustedPreviewHeight = previewFrame.height() * getWidth() / previewFrame.width();
  275 + int topInFrame = ((adjustedPreviewHeight - frameRect.height()) / 2)
  276 + * getWidth() / previewFrame.width();
  277 + int bottomInFrame = ((adjustedPreviewHeight + frameRect.height()) / 2) * getWidth()
  278 + / previewFrame.width();
  279 +
  280 + // 等比例投射到照片当中。
  281 + top = topInFrame * height / previewFrame.height();
  282 + bottom = bottomInFrame * height / previewFrame.height();
  283 + } else {
  284 + // 宽度大于图片
  285 + if (previewFrame.left < 0) {
  286 + // 高度对齐
  287 + int adjustedPreviewWidth = previewFrame.width() * getHeight() / previewFrame.height();
  288 + int leftInFrame = ((adjustedPreviewWidth - maskView.getFrameRect().width()) / 2) * getHeight()
  289 + / previewFrame.height();
  290 + int rightInFrame = ((adjustedPreviewWidth + maskView.getFrameRect().width()) / 2) * getHeight()
  291 + / previewFrame.height();
  292 +
  293 + // 等比例投射到照片当中。
  294 + left = leftInFrame * width / previewFrame.width();
  295 + right = rightInFrame * width / previewFrame.width();
  296 + }
  297 + }
  298 +
  299 + Rect region = new Rect();
  300 + region.left = left;
  301 + region.top = top;
  302 + region.right = right;
  303 + region.bottom = bottom;
  304 +
  305 + // 90度或者270度旋转
  306 + if (rotation % 180 == 90) {
  307 + int x = decoder.getWidth() / 2;
  308 + int y = decoder.getHeight() / 2;
  309 +
  310 + int rotatedWidth = region.height();
  311 + int rotated = region.width();
  312 +
  313 + // 计算,裁剪框旋转后的坐标
  314 + region.left = x - rotatedWidth / 2;
  315 + region.top = y - rotated / 2;
  316 + region.right = x + rotatedWidth / 2;
  317 + region.bottom = y + rotated / 2;
  318 + region.sort();
  319 + }
  320 +
  321 + BitmapFactory.Options options = new BitmapFactory.Options();
  322 +
  323 + // 最大图片大小。
  324 + int maxPreviewImageSize = 2560;
  325 + int size = Math.min(decoder.getWidth(), decoder.getHeight());
  326 + size = Math.min(size, maxPreviewImageSize);
  327 +
  328 + options.inSampleSize = ImageUtil.calculateInSampleSize(options, size, size);
  329 + options.inScaled = true;
  330 + options.inDensity = Math.max(options.outWidth, options.outHeight);
  331 + options.inTargetDensity = size;
  332 + options.inPreferredConfig = Bitmap.Config.RGB_565;
  333 +
  334 + Bitmap bitmap = decoder.decodeRegion(region, options);
  335 +
  336 + if (rotation != 0) {
  337 + // 只能是裁剪完之后再旋转了。有没有别的更好的方案呢?
  338 + Matrix matrix = new Matrix();
  339 + matrix.postRotate(rotation);
  340 + Bitmap rotatedBitmap = Bitmap.createBitmap(
  341 + bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
  342 + if (bitmap != rotatedBitmap) {
  343 + // 有时候 createBitmap会复用对象
  344 + bitmap.recycle();
  345 + }
  346 + bitmap = rotatedBitmap;
  347 + }
  348 +
  349 + final int status;
  350 +
  351 + // 调用本地质量控制请求
  352 + switch (maskType) {
  353 + case MaskView.MASK_TYPE_ID_CARD_FRONT:
  354 + status = IDcardQualityProcess.getInstance().idcardQualityDetectionImg(bitmap, true);
  355 + break;
  356 + case MaskView.MASK_TYPE_ID_CARD_BACK:
  357 + status = IDcardQualityProcess.getInstance().idcardQualityDetectionImg(bitmap, false);
  358 + break;
  359 + default:
  360 + status = 1;
  361 + }
  362 +
  363 + // 当有某个扫描处理线程调用成功后,阻止其他线程继续调用本地控制代码
  364 + if (status == SCAN_SUCCESS) {
  365 + // 扫描成功阻止多线程同时回调
  366 + if (!cameraControl.getAbortingScan().compareAndSet(false, true)) {
  367 + bitmap.recycle();
  368 + return 0;
  369 + }
  370 + autoPictureCallback.onPictureTaken(bitmap);
  371 + }
  372 +
  373 + showTipMessage(status);
  374 +
  375 + return status;
  376 + }
  377 +
  378 + private void showTipMessage(final int status) {
  379 + // 提示tip文字变化
  380 + uiHandler.post(new Runnable() {
  381 + @Override
  382 + public void run() {
  383 + if (status == 0) {
  384 + hintViewText.setVisibility(View.INVISIBLE);
  385 + } else if (!cameraControl.getAbortingScan().get()) {
  386 + hintViewText.setVisibility(View.VISIBLE);
  387 + hintViewText.setText(getScanMessage(status));
  388 + }
  389 + }
  390 + });
  391 + }
  392 +
  393 + private String getScanMessage(int status) {
  394 + String message;
  395 + switch (status) {
  396 + case 0:
  397 + message = "";
  398 + break;
  399 + case 2:
  400 + message = "身份证模糊,请重新尝试";
  401 + break;
  402 + case 3:
  403 + message = "身份证反光,请重新尝试";
  404 + break;
  405 + case 4:
  406 + message = "请将身份证前后反转再进行识别";
  407 + break;
  408 + case 5:
  409 + message = "请拿稳镜头和身份证";
  410 + break;
  411 + case 6:
  412 + message = "请将镜头靠近身份证";
  413 + break;
  414 + case 7:
  415 + message = "请将身份证完整置于取景框内";
  416 + break;
  417 + case NATIVE_AUTH_FAIL:
  418 + message = "本地质量控制授权失败";
  419 + break;
  420 + case NATIVE_INIT_FAIL:
  421 + message = "本地模型加载失败";
  422 + break;
  423 + case NATIVE_SOLOAD_FAIL:
  424 + message = "本地SO库加载失败";
  425 + break;
  426 + case 1:
  427 + default:
  428 + message = "请将身份证置于取景框内";
  429 + }
  430 +
  431 +
  432 + return message;
  433 + }
  434 +
  435 + private void init() {
  436 +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  437 +// cameraControl = new Camera2Control(getContext());
  438 +// } else {
  439 +//
  440 +// }
  441 + cameraControl = new Camera1Control(getContext());
  442 +
  443 + displayView = cameraControl.getDisplayView();
  444 + addView(displayView);
  445 +
  446 + maskView = new MaskView(getContext());
  447 + addView(maskView);
  448 +
  449 + hintView = new ImageView(getContext());
  450 + addView(hintView);
  451 +
  452 + hintViewTextWrapper = new LinearLayout(getContext());
  453 + hintViewTextWrapper.setOrientation(LinearLayout.VERTICAL);
  454 + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
  455 + PxUtil.dp2px(25));
  456 +
  457 + lp.gravity = Gravity.CENTER;
  458 + hintViewText = new TextView(getContext());
  459 + hintViewText.setBackgroundResource(R.drawable.bd_ocr_round_corner);
  460 + hintViewText.setAlpha(0.5f);
  461 + hintViewText.setPadding(PxUtil.dp2px(10), 0, PxUtil.dp2px(10), 0);
  462 + hintViewTextWrapper.addView(hintViewText, lp);
  463 +
  464 +
  465 + hintViewText.setGravity(Gravity.CENTER);
  466 + hintViewText.setTextColor(Color.WHITE);
  467 + hintViewText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
  468 + hintViewText.setText(getScanMessage(-1));
  469 +
  470 +
  471 + addView(hintViewTextWrapper, lp);
  472 + }
  473 +
  474 + @Override
  475 + protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  476 + displayView.layout(left, 0, right, bottom - top);
  477 + maskView.layout(left, 0, right, bottom - top);
  478 +
  479 + int hintViewWidth = PxUtil.dp2px(250);
  480 + int hintViewHeight = PxUtil.dp2px(25);
  481 +
  482 + int hintViewLeft = (getWidth() - hintViewWidth) / 2;
  483 + int hintViewTop = maskView.getFrameRect().bottom + PxUtil.dp2px(16);
  484 +
  485 + hintViewTextWrapper.layout(hintViewLeft, hintViewTop,
  486 + hintViewLeft + hintViewWidth, hintViewTop + hintViewHeight);
  487 +
  488 + hintView.layout(hintViewLeft, hintViewTop,
  489 + hintViewLeft + hintViewWidth, hintViewTop + hintViewHeight);
  490 + }
  491 +
  492 + /**
  493 + * 拍摄后的照片。需要进行裁剪。有些手机(比如三星)不会对照片数据进行旋转,而是将旋转角度写入EXIF信息当中,
  494 + * 所以需要做旋转处理。
  495 + *
  496 + * @param outputFile 写入照片的文件。
  497 + * @param data 原始照片数据。
  498 + * @param rotation 照片exif中的旋转角度。
  499 + * @return 裁剪好的bitmap。
  500 + */
  501 + @SuppressWarnings("ResultOfMethodCallIgnored")
  502 + private Bitmap crop(File outputFile, byte[] data, int rotation) {
  503 + try {
  504 + // BitmapRegionDecoder不会将整个图片加载到内存。
  505 + BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(data, 0, data.length, true);
  506 +
  507 + Rect previewFrame = cameraControl.getPreviewFrame();
  508 +
  509 + int width = rotation % 180 == 0 ? decoder.getWidth() : decoder.getHeight();
  510 + int height = rotation % 180 == 0 ? decoder.getHeight() : decoder.getWidth();
  511 +
  512 + Rect frameRect = maskView.getFrameRect();
  513 +
  514 + int left = width * frameRect.left / maskView.getWidth();
  515 + int top = height * frameRect.top / maskView.getHeight();
  516 + int right = width * frameRect.right / maskView.getWidth();
  517 + int bottom = height * frameRect.bottom / maskView.getHeight();
  518 +
  519 + // 高度大于图片
  520 + if (previewFrame.top < 0) {
  521 + // 宽度对齐。
  522 + int adjustedPreviewHeight = previewFrame.height() * getWidth() / previewFrame.width();
  523 + int topInFrame = ((adjustedPreviewHeight - frameRect.height()) / 2)
  524 + * getWidth() / previewFrame.width();
  525 + int bottomInFrame = ((adjustedPreviewHeight + frameRect.height()) / 2) * getWidth()
  526 + / previewFrame.width();
  527 +
  528 + // 等比例投射到照片当中。
  529 + top = topInFrame * height / previewFrame.height();
  530 + bottom = bottomInFrame * height / previewFrame.height();
  531 + } else {
  532 + // 宽度大于图片
  533 + if (previewFrame.left < 0) {
  534 + // 高度对齐
  535 + int adjustedPreviewWidth = previewFrame.width() * getHeight() / previewFrame.height();
  536 + int leftInFrame = ((adjustedPreviewWidth - maskView.getFrameRect().width()) / 2) * getHeight()
  537 + / previewFrame.height();
  538 + int rightInFrame = ((adjustedPreviewWidth + maskView.getFrameRect().width()) / 2) * getHeight()
  539 + / previewFrame.height();
  540 +
  541 + // 等比例投射到照片当中。
  542 + left = leftInFrame * width / previewFrame.width();
  543 + right = rightInFrame * width / previewFrame.width();
  544 + }
  545 + }
  546 +
  547 + Rect region = new Rect();
  548 + region.left = left;
  549 + region.top = top;
  550 + region.right = right;
  551 + region.bottom = bottom;
  552 +
  553 + // 90度或者270度旋转
  554 + if (rotation % 180 == 90) {
  555 + int x = decoder.getWidth() / 2;
  556 + int y = decoder.getHeight() / 2;
  557 +
  558 + int rotatedWidth = region.height();
  559 + int rotated = region.width();
  560 +
  561 + // 计算,裁剪框旋转后的坐标
  562 + region.left = x - rotatedWidth / 2;
  563 + region.top = y - rotated / 2;
  564 + region.right = x + rotatedWidth / 2;
  565 + region.bottom = y + rotated / 2;
  566 + region.sort();
  567 + }
  568 +
  569 + BitmapFactory.Options options = new BitmapFactory.Options();
  570 +
  571 + // 最大图片大小。
  572 + int maxPreviewImageSize = 2560;
  573 + int size = Math.min(decoder.getWidth(), decoder.getHeight());
  574 + size = Math.min(size, maxPreviewImageSize);
  575 +
  576 + options.inSampleSize = ImageUtil.calculateInSampleSize(options, size, size);
  577 + options.inScaled = true;
  578 + options.inDensity = Math.max(options.outWidth, options.outHeight);
  579 + options.inTargetDensity = size;
  580 + options.inPreferredConfig = Bitmap.Config.RGB_565;
  581 + Bitmap bitmap = decoder.decodeRegion(region, options);
  582 +
  583 + if (rotation != 0) {
  584 + // 只能是裁剪完之后再旋转了。有没有别的更好的方案呢?
  585 + Matrix matrix = new Matrix();
  586 + matrix.postRotate(rotation);
  587 + Bitmap rotatedBitmap = Bitmap.createBitmap(
  588 + bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
  589 + if (bitmap != rotatedBitmap) {
  590 + // 有时候 createBitmap会复用对象
  591 + bitmap.recycle();
  592 + }
  593 + bitmap = rotatedBitmap;
  594 + }
  595 +
  596 + try {
  597 + if (!outputFile.exists()) {
  598 + outputFile.createNewFile();
  599 + }
  600 + FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
  601 + bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileOutputStream);
  602 + fileOutputStream.flush();
  603 + fileOutputStream.close();
  604 + return bitmap;
  605 + } catch (IOException e) {
  606 + if (e!=null)
  607 + e.printStackTrace();
  608 + }
  609 + } catch (IOException e) {
  610 + if (e!=null)
  611 + e.printStackTrace();
  612 + }
  613 + return null;
  614 + }
  615 +
  616 + public void release() {
  617 + IDcardQualityProcess.getInstance().releaseModel();
  618 + }
  619 +
  620 + private class CameraViewTakePictureCallback implements ICameraControl.OnTakePictureCallback {
  621 +
  622 + private File file;
  623 + private OnTakePictureCallback callback;
  624 +
  625 + @Override
  626 + public void onPictureTaken(final byte[] data) {
  627 + CameraThreadPool.execute(new Runnable() {
  628 + @Override
  629 + public void run() {
  630 + final int rotation = ImageUtil.getOrientation(data);
  631 + Bitmap bitmap = crop(file, data, rotation);
  632 + callback.onPictureTaken(bitmap);
  633 + }
  634 + });
  635 + }
  636 + }
  637 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/camera/ICameraControl.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.camera;
  5 +
  6 +import android.graphics.Rect;
  7 +import android.view.View;
  8 +
  9 +import java.util.concurrent.atomic.AtomicBoolean;
  10 +
  11 +import androidx.annotation.IntDef;
  12 +
  13 +/**
  14 + * Android 5.0 相机的API发生很大的变化。些类屏蔽掉了 api的变化。相机的操作和功能,抽象剥离出来。
  15 + */
  16 +public interface ICameraControl {
  17 +
  18 + /**
  19 + * 闪光灯关 {@link #setFlashMode(int)}
  20 + */
  21 + int FLASH_MODE_OFF = 0;
  22 + /**
  23 + * 闪光灯开 {@link #setFlashMode(int)}
  24 + */
  25 + int FLASH_MODE_TORCH = 1;
  26 + /**
  27 + * 闪光灯自动 {@link #setFlashMode(int)}
  28 + */
  29 + int FLASH_MODE_AUTO = 2;
  30 +
  31 + @IntDef({FLASH_MODE_TORCH, FLASH_MODE_OFF, FLASH_MODE_AUTO})
  32 + @interface FlashMode {
  33 +
  34 + }
  35 +
  36 + /**
  37 + * 照相回调。
  38 + */
  39 + interface OnTakePictureCallback {
  40 + void onPictureTaken(byte[] data);
  41 + }
  42 +
  43 + /**
  44 + * 设置本地质量控制回调,如果不设置则视为不扫描调用本地质量控制代码。
  45 + */
  46 + void setDetectCallback(OnDetectPictureCallback callback);
  47 +
  48 + /**
  49 + * 预览回调
  50 + */
  51 + interface OnDetectPictureCallback {
  52 + int onDetect(byte[] data, int rotation);
  53 + }
  54 +
  55 + /**
  56 + * 打开相机。
  57 + */
  58 + void start();
  59 +
  60 + /**
  61 + * 关闭相机
  62 + */
  63 + void stop();
  64 +
  65 + void pause();
  66 +
  67 + void resume();
  68 +
  69 + /**
  70 + * 相机对应的预览视图。
  71 + * @return 预览视图
  72 + */
  73 + View getDisplayView();
  74 +
  75 + /**
  76 + * 看到的预览可能不是照片的全部。返回预览视图的全貌。
  77 + * @return 预览视图frame;
  78 + */
  79 + Rect getPreviewFrame();
  80 +
  81 + /**
  82 + * 拍照。结果在回调中获取。
  83 + * @param callback 拍照结果回调
  84 + */
  85 + void takePicture(OnTakePictureCallback callback);
  86 +
  87 + /**
  88 + * 设置权限回调,当手机没有拍照权限时,可在回调中获取。
  89 + * @param callback 权限回调
  90 + */
  91 + void setPermissionCallback(PermissionCallback callback);
  92 +
  93 + /**
  94 + * 设置水平方向
  95 + * @param displayOrientation 参数值见 {@link com.cnlive.ocr.ui.camera.CameraView.Orientation}
  96 + */
  97 + void setDisplayOrientation(@CameraView.Orientation int displayOrientation);
  98 +
  99 + /**
  100 + * 获取到拍照权限时,调用些函数以继续。
  101 + */
  102 + void refreshPermission();
  103 +
  104 + /**
  105 + * 获取已经扫描成功,处理中
  106 + */
  107 + AtomicBoolean getAbortingScan();
  108 +
  109 + /**
  110 + * 设置闪光灯状态。
  111 + * @param flashMode {@link #FLASH_MODE_TORCH,#FLASH_MODE_OFF,#FLASH_MODE_AUTO}
  112 + */
  113 + void setFlashMode(@FlashMode int flashMode);
  114 +
  115 + /**
  116 + * 获取当前闪光灯状态
  117 + * @return 当前闪光灯状态 参见 {@link #setFlashMode(int)}
  118 + */
  119 + @FlashMode
  120 + int getFlashMode();
  121 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/camera/MaskView.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.camera;
  5 +
  6 +import android.content.Context;
  7 +import android.graphics.Canvas;
  8 +import android.graphics.Color;
  9 +import android.graphics.Paint;
  10 +import android.graphics.Path;
  11 +import android.graphics.PorterDuff;
  12 +import android.graphics.PorterDuffXfermode;
  13 +import android.graphics.Rect;
  14 +import android.graphics.drawable.Drawable;
  15 +import android.os.Build;
  16 +import android.util.AttributeSet;
  17 +import android.view.View;
  18 +
  19 +import com.cnlive.core.people_verfication.R;
  20 +
  21 +import java.io.File;
  22 +
  23 +import androidx.annotation.IntDef;
  24 +import androidx.annotation.RequiresApi;
  25 +import androidx.core.content.res.ResourcesCompat;
  26 +
  27 +@SuppressWarnings("unused")
  28 +public class MaskView extends View {
  29 +
  30 + public static final int MASK_TYPE_NONE = 0;
  31 + public static final int MASK_TYPE_ID_CARD_FRONT = 1;
  32 + public static final int MASK_TYPE_ID_CARD_BACK = 2;
  33 + public static final int MASK_TYPE_BANK_CARD = 11;
  34 + public static final int MASK_TYPE_AVATAR = 12;
  35 +
  36 + @IntDef({MASK_TYPE_NONE, MASK_TYPE_ID_CARD_FRONT, MASK_TYPE_ID_CARD_BACK, MASK_TYPE_BANK_CARD, MASK_TYPE_AVATAR})
  37 + @interface MaskType {
  38 +
  39 + }
  40 +
  41 + public void setLineColor(int lineColor) {
  42 + this.lineColor = lineColor;
  43 + }
  44 +
  45 + public void setMaskColor(int maskColor) {
  46 + this.maskColor = maskColor;
  47 + }
  48 +
  49 + private int lineColor = Color.WHITE;
  50 +
  51 + private int maskType = MASK_TYPE_ID_CARD_FRONT;
  52 +
  53 + private int maskColor = Color.argb(100, 0, 0, 0);
  54 +
  55 + private Paint eraser = new Paint(Paint.ANTI_ALIAS_FLAG);
  56 + private Paint pen = new Paint(Paint.ANTI_ALIAS_FLAG);
  57 +
  58 + private Rect frame = new Rect();
  59 +
  60 + private Drawable locatorDrawable;
  61 +
  62 + public Rect getFrameRect() {
  63 + if (maskType == MASK_TYPE_NONE) {
  64 + return new Rect(0, 0, getWidth(), getHeight());
  65 + } else {
  66 + return new Rect(frame);
  67 + }
  68 +
  69 + }
  70 +
  71 + public Rect getFrameRectExtend() {
  72 + Rect rc = new Rect(frame);
  73 + int widthExtend = (int) ((frame.right - frame.left) * 0.02f);
  74 + int heightExtend = (int) ((frame.bottom - frame.top) * 0.02f);
  75 + rc.left -= widthExtend;
  76 + rc.right += widthExtend;
  77 + rc.top -= heightExtend;
  78 + rc.bottom += heightExtend;
  79 + return rc;
  80 + }
  81 +
  82 + public void setMaskType(@MaskType int maskType) {
  83 + this.maskType = maskType;
  84 + switch (maskType) {
  85 + case MASK_TYPE_ID_CARD_FRONT:
  86 + locatorDrawable = ResourcesCompat.getDrawable(getResources(),
  87 + R.drawable.bd_ocr_id_card_locator_front, null);
  88 + break;
  89 + case MASK_TYPE_ID_CARD_BACK:
  90 + locatorDrawable = ResourcesCompat.getDrawable(getResources(),
  91 + R.drawable.bd_ocr_id_card_locator_back, null);
  92 + break;
  93 + case MASK_TYPE_BANK_CARD:
  94 + break;
  95 + case MASK_TYPE_AVATAR:
  96 + break;
  97 + case MASK_TYPE_NONE:
  98 + default:
  99 + break;
  100 + }
  101 + invalidate();
  102 + }
  103 +
  104 + public int getMaskType() {
  105 + return maskType;
  106 + }
  107 +
  108 + public void setOrientation(@CameraView.Orientation int orientation) {
  109 + }
  110 +
  111 + public MaskView(Context context) {
  112 + super(context);
  113 + init();
  114 + }
  115 +
  116 + public MaskView(Context context, AttributeSet attrs) {
  117 + super(context, attrs);
  118 + init();
  119 + }
  120 +
  121 + public MaskView(Context context, AttributeSet attrs, int defStyleAttr) {
  122 + super(context, attrs, defStyleAttr);
  123 + init();
  124 + }
  125 +
  126 + private void init() {
  127 + locatorDrawable = ResourcesCompat.getDrawable(getResources(), R.drawable.bd_ocr_id_card_locator_front, null);
  128 + }
  129 +
  130 + @Override
  131 + protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  132 + super.onSizeChanged(w, h, oldw, oldh);
  133 + if (w > 0 && h > 0) {
  134 + float ratio = h > w ? 0.9f : 0.72f;
  135 +
  136 + int width = (int) (w * ratio);
  137 + int height;
  138 + if (maskType == MASK_TYPE_AVATAR) {
  139 + height = width;
  140 + } else {
  141 + height = width * 400 / 620;
  142 + }
  143 +
  144 + int left = (w - width) / 2;
  145 + int top = (h - height) / 2;
  146 + int right = width + left;
  147 + int bottom = height + top;
  148 +
  149 + frame.left = left;
  150 + frame.top = top;
  151 + frame.right = right;
  152 + frame.bottom = bottom;
  153 +
  154 + }
  155 + }
  156 +
  157 + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
  158 + @Override
  159 + protected void onDraw(Canvas canvas) {
  160 + super.onDraw(canvas);
  161 +
  162 + int width = frame.width();
  163 + int height = frame.height();
  164 +
  165 + int left = frame.left;
  166 + int top = frame.top;
  167 + int right = frame.right;
  168 + int bottom = frame.bottom;
  169 +
  170 + canvas.drawColor(maskColor);
  171 + fillRectRound(left, top, right, bottom, 30, 30, false);
  172 + canvas.drawPath(path, pen);
  173 + canvas.drawPath(path, eraser);
  174 +
  175 + if (maskType == MASK_TYPE_ID_CARD_FRONT) {
  176 + locatorDrawable.setBounds(
  177 + (int) (left + 601f / 1006 * width),
  178 + (int) (top + (110f / 632) * height),
  179 + (int) (left + (963f / 1006) * width),
  180 + (int) (top + (476f / 632) * height));
  181 + } else if (maskType == MASK_TYPE_ID_CARD_BACK) {
  182 + locatorDrawable.setBounds(
  183 + (int) (left + 51f / 1006 * width),
  184 + (int) (top + (48f / 632) * height),
  185 + (int) (left + (250f / 1006) * width),
  186 + (int) (top + (262f / 632) * height));
  187 + }
  188 + if (locatorDrawable != null) {
  189 + locatorDrawable.draw(canvas);
  190 + }
  191 + }
  192 +
  193 + private Path path = new Path();
  194 +
  195 + private Path fillRectRound(float left, float top, float right, float bottom, float rx, float ry, boolean
  196 + conformToOriginalPost) {
  197 +
  198 + path.reset();
  199 + if (rx < 0) {
  200 + rx = 0;
  201 + }
  202 + if (ry < 0) {
  203 + ry = 0;
  204 + }
  205 + float width = right - left;
  206 + float height = bottom - top;
  207 + if (rx > width / 2) {
  208 + rx = width / 2;
  209 + }
  210 + if (ry > height / 2) {
  211 + ry = height / 2;
  212 + }
  213 + float widthMinusCorners = (width - (2 * rx));
  214 + float heightMinusCorners = (height - (2 * ry));
  215 +
  216 + path.moveTo(right, top + ry);
  217 + path.rQuadTo(0, -ry, -rx, -ry);
  218 + path.rLineTo(-widthMinusCorners, 0);
  219 + path.rQuadTo(-rx, 0, -rx, ry);
  220 + path.rLineTo(0, heightMinusCorners);
  221 +
  222 + if (conformToOriginalPost) {
  223 + path.rLineTo(0, ry);
  224 + path.rLineTo(width, 0);
  225 + path.rLineTo(0, -ry);
  226 + } else {
  227 + path.rQuadTo(0, ry, rx, ry);
  228 + path.rLineTo(widthMinusCorners, 0);
  229 + path.rQuadTo(rx, 0, rx, -ry);
  230 + }
  231 +
  232 + path.rLineTo(0, -heightMinusCorners);
  233 + path.close();
  234 + return path;
  235 + }
  236 +
  237 + {
  238 + // 硬件加速不支持,图层混合。
  239 + setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  240 +
  241 + pen.setColor(Color.WHITE);
  242 + pen.setStyle(Paint.Style.STROKE);
  243 + pen.setStrokeWidth(6);
  244 +
  245 + eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
  246 + }
  247 +
  248 + private void capture(File file) {
  249 +
  250 + }
  251 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/camera/OCRCameraLayout.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.camera;
  5 +
  6 +import android.content.Context;
  7 +import android.content.res.TypedArray;
  8 +import android.graphics.Canvas;
  9 +import android.graphics.Color;
  10 +import android.graphics.Paint;
  11 +import android.graphics.Rect;
  12 +import android.util.AttributeSet;
  13 +import android.view.View;
  14 +import android.widget.FrameLayout;
  15 +
  16 +import com.cnlive.core.people_verfication.R;
  17 +
  18 +
  19 +public class OCRCameraLayout extends FrameLayout {
  20 +
  21 + public static int ORIENTATION_PORTRAIT = 0;
  22 + public static int ORIENTATION_HORIZONTAL = 1;
  23 +
  24 + private int orientation = ORIENTATION_PORTRAIT;
  25 + private View contentView;
  26 + private View centerView;
  27 + private View leftDownView;
  28 + private View rightUpView;
  29 +
  30 + private int contentViewId;
  31 + private int centerViewId;
  32 + private int leftDownViewId;
  33 + private int rightUpViewId;
  34 +
  35 + public void setOrientation(int orientation) {
  36 + if (this.orientation == orientation) {
  37 + return;
  38 + }
  39 + this.orientation = orientation;
  40 + requestLayout();
  41 + }
  42 +
  43 + public OCRCameraLayout(Context context) {
  44 + super(context);
  45 + }
  46 +
  47 + public OCRCameraLayout(Context context, AttributeSet attrs) {
  48 + super(context, attrs);
  49 + parseAttrs(attrs);
  50 + }
  51 +
  52 + public OCRCameraLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  53 + super(context, attrs, defStyleAttr);
  54 + parseAttrs(attrs);
  55 + }
  56 +
  57 + {
  58 + setWillNotDraw(false);
  59 + }
  60 +
  61 + private void parseAttrs(AttributeSet attrs) {
  62 + TypedArray a = getContext().getTheme().obtainStyledAttributes(
  63 + attrs,
  64 + R.styleable.OCRCameraLayout,
  65 + 0, 0);
  66 + try {
  67 + contentViewId = a.getResourceId(R.styleable.OCRCameraLayout_contentView, -1);
  68 + centerViewId = a.getResourceId(R.styleable.OCRCameraLayout_centerView, -1);
  69 + leftDownViewId = a.getResourceId(R.styleable.OCRCameraLayout_leftDownView, -1);
  70 + rightUpViewId = a.getResourceId(R.styleable.OCRCameraLayout_rightUpView, -1);
  71 + } finally {
  72 + a.recycle();
  73 + }
  74 + }
  75 +
  76 + @Override
  77 + protected void onAttachedToWindow() {
  78 + super.onAttachedToWindow();
  79 + contentView = findViewById(contentViewId);
  80 + if (centerViewId != -1) {
  81 + centerView = findViewById(centerViewId);
  82 + }
  83 + leftDownView = findViewById(leftDownViewId);
  84 + rightUpView = findViewById(rightUpViewId);
  85 + }
  86 +
  87 + private Rect backgroundRect = new Rect();
  88 + private Paint paint = new Paint();
  89 +
  90 + {
  91 + paint.setStyle(Paint.Style.FILL);
  92 + paint.setColor(Color.argb(83, 0, 0, 0));
  93 + }
  94 +
  95 + @Override
  96 + protected void onLayout(boolean changed, int l, int t, int r, int b) {
  97 + int width = getWidth();
  98 + int height = getHeight();
  99 + int left;
  100 + int top;
  101 +
  102 + MarginLayoutParams leftDownViewLayoutParams = (MarginLayoutParams) leftDownView.getLayoutParams();
  103 + MarginLayoutParams rightUpViewLayoutParams = (MarginLayoutParams) rightUpView.getLayoutParams();
  104 + if (r < b) {
  105 + int contentHeight = width * 4 / 3;
  106 + int heightLeft = height - contentHeight;
  107 + contentView.layout(l, t, r, contentHeight);
  108 +
  109 + backgroundRect.left = 0;
  110 + backgroundRect.top = contentHeight;
  111 + backgroundRect.right = width;
  112 + backgroundRect.bottom = height;
  113 +
  114 + // layout centerView;
  115 + if (centerView != null) {
  116 + left = (width - centerView.getMeasuredWidth()) / 2;
  117 + top = contentHeight + (heightLeft - centerView.getMeasuredHeight()) / 2;
  118 + centerView
  119 + .layout(left, top, left + centerView.getMeasuredWidth(), top + centerView.getMeasuredHeight());
  120 + }
  121 + // layout leftDownView
  122 +
  123 + left = leftDownViewLayoutParams.leftMargin;
  124 + top = contentHeight + (heightLeft - leftDownView.getMeasuredHeight()) / 2;
  125 + leftDownView
  126 + .layout(left, top, left + leftDownView.getMeasuredWidth(), top + leftDownView.getMeasuredHeight());
  127 + // layout rightUpView
  128 + left = width - rightUpView.getMeasuredWidth() - rightUpViewLayoutParams.rightMargin;
  129 + top = contentHeight + (heightLeft - rightUpView.getMeasuredHeight()) / 2;
  130 + rightUpView.layout(left, top, left + rightUpView.getMeasuredWidth(), top + rightUpView.getMeasuredHeight());
  131 + } else {
  132 + int contentWidth = height * 4 / 3;
  133 + int widthLeft = width - contentWidth;
  134 + contentView.layout(l, t, contentWidth, height);
  135 +
  136 + backgroundRect.left = contentWidth;
  137 + backgroundRect.top = 0;
  138 + backgroundRect.right = width;
  139 + backgroundRect.bottom = height;
  140 +
  141 + // layout centerView
  142 + if (centerView != null) {
  143 + left = contentWidth + (widthLeft - centerView.getMeasuredWidth()) / 2;
  144 + top = (height - centerView.getMeasuredHeight()) / 2;
  145 + centerView
  146 + .layout(left, top, left + centerView.getMeasuredWidth(), top + centerView.getMeasuredHeight());
  147 + }
  148 + // layout leftDownView
  149 + left = contentWidth + (widthLeft - leftDownView.getMeasuredWidth()) / 2;
  150 + top = height - leftDownView.getMeasuredHeight() - leftDownViewLayoutParams.bottomMargin;
  151 + leftDownView
  152 + .layout(left, top, left + leftDownView.getMeasuredWidth(), top + leftDownView.getMeasuredHeight());
  153 + // layout rightUpView
  154 + left = contentWidth + (widthLeft - rightUpView.getMeasuredWidth()) / 2;
  155 +
  156 + top = rightUpViewLayoutParams.topMargin;
  157 + rightUpView.layout(left, top, left + rightUpView.getMeasuredWidth(), top + rightUpView.getMeasuredHeight());
  158 + }
  159 + }
  160 +
  161 + @Override
  162 + protected void onDraw(Canvas canvas) {
  163 + super.onDraw(canvas);
  164 + canvas.drawRect(backgroundRect, paint);
  165 + }
  166 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/camera/OCRFrameLayout.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.camera;
  5 +
  6 +
  7 +import android.content.Context;
  8 +import android.util.AttributeSet;
  9 +import android.view.View;
  10 +import android.view.ViewGroup;
  11 +
  12 +public class OCRFrameLayout extends ViewGroup {
  13 +
  14 + public OCRFrameLayout(Context context) {
  15 + super(context);
  16 + }
  17 +
  18 + public OCRFrameLayout(Context context, AttributeSet attrs) {
  19 + super(context, attrs);
  20 + parseAttrs(attrs);
  21 + }
  22 +
  23 + public OCRFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  24 + super(context, attrs, defStyleAttr);
  25 + parseAttrs(attrs);
  26 + }
  27 +
  28 + private void parseAttrs(AttributeSet attrs) {
  29 + }
  30 +
  31 + @Override
  32 + protected void onLayout(boolean changed, int l, int t, int r, int b) {
  33 + int childCount = getChildCount();
  34 + for (int i = 0; i < childCount; i++) {
  35 + View view = getChildAt(i);
  36 + view.layout(l, t, r, b);
  37 + }
  38 + }
  39 +
  40 + @Override
  41 + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  42 + super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  43 + int width;
  44 + int height;
  45 + int childCount = getChildCount();
  46 +
  47 + width = MeasureSpec.getSize(widthMeasureSpec);
  48 + height = MeasureSpec.getSize(heightMeasureSpec);
  49 + for (int i = 0; i < childCount; i++) {
  50 + View view = getChildAt(i);
  51 + measureChild(view, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec
  52 + (height, MeasureSpec.EXACTLY));
  53 + }
  54 + setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
  55 + }
  56 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/camera/PermissionCallback.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.camera;
  5 +
  6 +public interface PermissionCallback {
  7 + boolean onRequestPermission();
  8 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/crop/CropView.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.crop;
  5 +
  6 +import android.content.Context;
  7 +import android.graphics.Bitmap;
  8 +import android.graphics.BitmapFactory;
  9 +import android.graphics.Canvas;
  10 +import android.graphics.Matrix;
  11 +import android.graphics.Point;
  12 +import android.graphics.Rect;
  13 +import android.media.ExifInterface;
  14 +import android.util.AttributeSet;
  15 +import android.view.GestureDetector;
  16 +import android.view.MotionEvent;
  17 +import android.view.ScaleGestureDetector;
  18 +import android.view.View;
  19 +import android.view.WindowManager;
  20 +
  21 +import com.cnlive.core.libs.base.util.AlertUtil;
  22 +import com.cnlive.core.ocr.ui.util.ImageUtil;
  23 +
  24 +import java.io.IOException;
  25 +
  26 +public class CropView extends View {
  27 +
  28 + public CropView(Context context) {
  29 + super(context);
  30 + init();
  31 + }
  32 +
  33 + public CropView(Context context, AttributeSet attrs) {
  34 + super(context, attrs);
  35 + init();
  36 + }
  37 +
  38 + public CropView(Context context, AttributeSet attrs, int defStyleAttr) {
  39 + super(context, attrs, defStyleAttr);
  40 + init();
  41 + }
  42 +
  43 + public void setFilePath(String path) {
  44 +
  45 + if (this.bitmap != null && !this.bitmap.isRecycled()) {
  46 + this.bitmap.recycle();
  47 + }
  48 +
  49 + if (path == null) {
  50 + return;
  51 + }
  52 +
  53 + BitmapFactory.Options options = new BitmapFactory.Options();
  54 + options.inJustDecodeBounds = true;
  55 + Bitmap original = BitmapFactory.decodeFile(path, options);
  56 +
  57 + try {
  58 + ExifInterface exif = new ExifInterface(path);
  59 + int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
  60 + Matrix matrix = new Matrix();
  61 + int rotationInDegrees = ImageUtil.exifToDegrees(rotation);
  62 + if (rotation != 0f) {
  63 + matrix.preRotate(rotationInDegrees);
  64 + }
  65 +
  66 + // 图片太大会导致内存泄露,所以在显示前对图片进行裁剪。
  67 + int maxPreviewImageSize = 2560;
  68 +
  69 + int min = Math.min(options.outWidth, options.outHeight);
  70 + min = Math.min(min, maxPreviewImageSize);
  71 +
  72 + WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
  73 + Point screenSize = new Point();
  74 + windowManager.getDefaultDisplay().getSize(screenSize);
  75 + min = Math.min(min, screenSize.x * 2 / 3);
  76 +
  77 + options.inSampleSize = ImageUtil.calculateInSampleSize(options, min, min);
  78 + options.inScaled = true;
  79 + options.inDensity = options.outWidth;
  80 + options.inTargetDensity = min * options.inSampleSize;
  81 + options.inPreferredConfig = Bitmap.Config.RGB_565;
  82 +
  83 + options.inJustDecodeBounds = false;
  84 + this.bitmap = BitmapFactory.decodeFile(path, options);
  85 + } catch (IOException e) {
  86 + e.printStackTrace();
  87 + this.bitmap = original;
  88 + } catch (NullPointerException e) {
  89 + e.printStackTrace();
  90 + }
  91 + setBitmap(this.bitmap);
  92 + }
  93 +
  94 + private void setBitmap(Bitmap bitmap) {
  95 + this.bitmap = bitmap;
  96 + matrix.reset();
  97 + centerImage(getWidth(), getHeight());
  98 + rotation = 0;
  99 + invalidate();
  100 + }
  101 +
  102 + @Override
  103 + protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  104 + super.onSizeChanged(w, h, oldw, oldh);
  105 + centerImage(w, h);
  106 + invalidate();
  107 + }
  108 +
  109 + public Bitmap crop(Rect frame) {
  110 + float scale = getScale();
  111 +
  112 + float[] src = new float[] {frame.left, frame.top};
  113 + float[] desc = new float[] {0, 0};
  114 +
  115 + Matrix invertedMatrix = new Matrix();
  116 + this.matrix.invert(invertedMatrix);
  117 + invertedMatrix.mapPoints(desc, src);
  118 +
  119 + Matrix matrix = new Matrix();
  120 +
  121 + int width = (int) (frame.width() / scale);
  122 + int height = (int) (frame.height() / scale);
  123 + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
  124 + Canvas canvas = new Canvas(bitmap);
  125 +
  126 + Bitmap originalBitmap = this.bitmap;
  127 + if(originalBitmap == null){
  128 + AlertUtil.showDeftToast(getContext(),"该照片已失效,请重新选择");
  129 + return null;
  130 + }
  131 + matrix.postTranslate(-desc[0], -desc[1]);
  132 + canvas.drawBitmap(originalBitmap, matrix, null);
  133 + return bitmap;
  134 + }
  135 +
  136 + public void setMinimumScale(float setMinimumScale) {
  137 + this.setMinimumScale = setMinimumScale;
  138 + }
  139 +
  140 + public void setMaximumScale(float maximumScale) {
  141 + this.maximumScale = maximumScale;
  142 + }
  143 +
  144 + private float setMinimumScale = 0.2f;
  145 + private float maximumScale = 4.0f;
  146 +
  147 + private float[] matrixArray = new float[9];
  148 + private Matrix matrix = new Matrix();
  149 + private Bitmap bitmap;
  150 +
  151 + private GestureDetector gestureDetector;
  152 +
  153 + private ScaleGestureDetector scaleGestureDetector;
  154 + private ScaleGestureDetector.OnScaleGestureListener onScaleGestureListener =
  155 + new ScaleGestureDetector.OnScaleGestureListener() {
  156 + @Override
  157 + public boolean onScale(ScaleGestureDetector detector) {
  158 + scale(detector);
  159 + return true;
  160 + }
  161 +
  162 + @Override
  163 + public boolean onScaleBegin(ScaleGestureDetector detector) {
  164 + return true;
  165 + }
  166 +
  167 + @Override
  168 + public void onScaleEnd(ScaleGestureDetector detector) {
  169 + float scale = detector.getScaleFactor();
  170 + matrix.postScale(scale, scale);
  171 + invalidate();
  172 + }
  173 + };
  174 +
  175 + private void init() {
  176 + scaleGestureDetector = new ScaleGestureDetector(getContext(), onScaleGestureListener);
  177 + gestureDetector = new GestureDetector(getContext(), new GestureDetector.OnGestureListener() {
  178 + @Override
  179 + public boolean onDown(MotionEvent e) {
  180 + return true;
  181 + }
  182 +
  183 + @Override
  184 + public void onShowPress(MotionEvent e) {
  185 + }
  186 +
  187 + @Override
  188 + public boolean onSingleTapUp(MotionEvent e) {
  189 + return false;
  190 + }
  191 +
  192 + @Override
  193 + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
  194 + translate(distanceX, distanceY);
  195 + return true;
  196 + }
  197 +
  198 + @Override
  199 + public void onLongPress(MotionEvent e) {
  200 + }
  201 +
  202 + @Override
  203 + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
  204 + return false;
  205 + }
  206 + });
  207 + }
  208 +
  209 + int rotation = 0;
  210 +
  211 + public void rotate(int degrees) {
  212 + Matrix matrix = new Matrix();
  213 +
  214 + if(bitmap == null) return;
  215 + int dx = this.bitmap.getWidth() / 2;
  216 + int dy = this.bitmap.getHeight() / 2;
  217 +
  218 + matrix.postTranslate(-dx, -dy);
  219 + matrix.postRotate(degrees);
  220 + matrix.postTranslate(dy, dx);
  221 + Bitmap scaledBitmap = this.bitmap;
  222 + Bitmap rotatedBitmap = Bitmap.createBitmap(scaledBitmap.getHeight(), scaledBitmap.getWidth(),
  223 + Bitmap.Config.RGB_565);
  224 + Canvas canvas = new Canvas(rotatedBitmap);
  225 + canvas.drawBitmap(this.bitmap, matrix, null);
  226 + this.bitmap.recycle();
  227 + this.bitmap = rotatedBitmap;
  228 + centerImage(getWidth(), getHeight());
  229 + invalidate();
  230 + }
  231 +
  232 + private void translate(float distanceX, float distanceY) {
  233 + matrix.getValues(matrixArray);
  234 + float left = matrixArray[Matrix.MTRANS_X];
  235 + float top = matrixArray[Matrix.MTRANS_Y];
  236 +
  237 + Rect bound = getRestrictedBound();
  238 + if (bound != null) {
  239 + float scale = getScale();
  240 + float right = left + (int) (bitmap.getWidth() / scale);
  241 + float bottom = top + (int) (bitmap.getHeight() / scale);
  242 +
  243 + if (left - distanceX > bound.left) {
  244 + distanceX = left - bound.left;
  245 + }
  246 + if (top - distanceY > bound.top) {
  247 + distanceY = top - bound.top;
  248 + }
  249 +
  250 + if (distanceX > 0) {
  251 + if (right - distanceX < bound.right) {
  252 + distanceX = right - bound.right;
  253 + }
  254 + }
  255 + if (distanceY > 0) {
  256 + if (bottom - distanceY < bound.bottom) {
  257 + distanceY = bottom - bound.bottom;
  258 + }
  259 + }
  260 + }
  261 + matrix.postTranslate(-distanceX, -distanceY);
  262 + invalidate();
  263 + }
  264 +
  265 + private void scale(ScaleGestureDetector detector) {
  266 + float scale = detector.getScaleFactor();
  267 + float currentScale = getScale();
  268 + if (currentScale * scale < setMinimumScale) {
  269 + scale = setMinimumScale / currentScale;
  270 + }
  271 + if (currentScale * scale > maximumScale) {
  272 + scale = maximumScale / currentScale;
  273 + }
  274 + matrix.postScale(scale, scale, detector.getFocusX(), detector.getFocusY());
  275 + invalidate();
  276 + }
  277 +
  278 + private void centerImage(int width, int height) {
  279 + if (width <= 0 || height <= 0 || bitmap == null) {
  280 + return;
  281 + }
  282 + float widthRatio = 1.0f * height / this.bitmap.getHeight();
  283 + float heightRatio = 1.0f * width / this.bitmap.getWidth();
  284 +
  285 + float ratio = Math.min(widthRatio, heightRatio);
  286 +
  287 + float dx = (width - this.bitmap.getWidth()) / 2;
  288 + float dy = (height - this.bitmap.getHeight()) / 2;
  289 + matrix.setTranslate(0, 0);
  290 + matrix.setScale(ratio, ratio, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
  291 + matrix.postTranslate(dx, dy);
  292 + invalidate();
  293 + }
  294 +
  295 + private float getScale() {
  296 + matrix.getValues(matrixArray);
  297 + float scale = matrixArray[Matrix.MSCALE_X];
  298 + if (Math.abs(scale) <= 0.1) {
  299 + scale = matrixArray[Matrix.MSKEW_X];
  300 + }
  301 + return Math.abs(scale);
  302 + }
  303 +
  304 + @Override
  305 + protected void onDraw(Canvas canvas) {
  306 + super.onDraw(canvas);
  307 + if (bitmap != null) {
  308 + canvas.drawBitmap(bitmap, matrix, null);
  309 + }
  310 + }
  311 +
  312 + @Override
  313 + public boolean onTouchEvent(MotionEvent event) {
  314 + boolean result = scaleGestureDetector.onTouchEvent(event);
  315 + result = gestureDetector.onTouchEvent(event) || result;
  316 + return result || super.onTouchEvent(event);
  317 + }
  318 +
  319 + private Rect restrictBound;
  320 +
  321 + private Rect getRestrictedBound() {
  322 + return restrictBound;
  323 + }
  324 +
  325 + public void setRestrictBound(Rect rect) {
  326 + this.restrictBound = rect;
  327 + }
  328 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/crop/FrameOverlayView.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.crop;
  5 +
  6 +
  7 +import android.content.Context;
  8 +import android.graphics.Canvas;
  9 +import android.graphics.Color;
  10 +import android.graphics.Paint;
  11 +import android.graphics.PorterDuff;
  12 +import android.graphics.PorterDuffXfermode;
  13 +import android.graphics.Rect;
  14 +import android.graphics.RectF;
  15 +import android.util.AttributeSet;
  16 +import android.view.GestureDetector;
  17 +import android.view.MotionEvent;
  18 +import android.view.View;
  19 +
  20 +import com.cnlive.core.ocr.ui.util.PxUtil;
  21 +
  22 +
  23 +public class FrameOverlayView extends View {
  24 +
  25 + interface OnFrameChangeListener {
  26 + void onFrameChange(RectF newFrame);
  27 + }
  28 +
  29 + public Rect getFrameRect() {
  30 + Rect rect = new Rect();
  31 + rect.left = (int) frameRect.left;
  32 + rect.top = (int) frameRect.top;
  33 + rect.right = (int) frameRect.right;
  34 + rect.bottom = (int) frameRect.bottom;
  35 + return rect;
  36 + }
  37 +
  38 + public FrameOverlayView(Context context) {
  39 + super(context);
  40 + init();
  41 + }
  42 +
  43 + public FrameOverlayView(Context context, AttributeSet attrs) {
  44 + super(context, attrs);
  45 + init();
  46 + }
  47 +
  48 + public FrameOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
  49 + super(context, attrs, defStyleAttr);
  50 + init();
  51 + }
  52 +
  53 + private GestureDetector.SimpleOnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener() {
  54 + @Override
  55 + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
  56 + translate(distanceX, distanceY);
  57 + return true;
  58 + }
  59 +
  60 + };
  61 +
  62 + private static final int CORNER_LEFT_TOP = 1;
  63 + private static final int CORNER_RIGHT_TOP = 2;
  64 + private static final int CORNER_RIGHT_BOTTOM = 3;
  65 + private static final int CORNER_LEFT_BOTTOM = 4;
  66 +
  67 + private int currentCorner = -1;
  68 + int margin = 20;
  69 + int cornerLength = 100;
  70 + int cornerLineWidth = 6;
  71 +
  72 + private int maskColor = Color.argb(180, 0, 0, 0);
  73 +
  74 + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
  75 + private Paint eraser = new Paint(Paint.ANTI_ALIAS_FLAG);
  76 + private GestureDetector gestureDetector;
  77 + private RectF touchRect = new RectF();
  78 + private RectF frameRect = new RectF();
  79 +
  80 + private OnFrameChangeListener onFrameChangeListener;
  81 +
  82 + {
  83 + setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  84 + paint.setColor(Color.WHITE);
  85 + paint.setStyle(Paint.Style.STROKE);
  86 + paint.setStrokeWidth(6);
  87 +
  88 + eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
  89 + }
  90 +
  91 + public void setOnFrameChangeListener(OnFrameChangeListener onFrameChangeListener) {
  92 + this.onFrameChangeListener = onFrameChangeListener;
  93 + }
  94 +
  95 + private void init() {
  96 + gestureDetector = new GestureDetector(getContext(), onGestureListener);
  97 + cornerLength = PxUtil.dp2px(18);
  98 + cornerLineWidth = PxUtil.dp2px(3);
  99 + }
  100 +
  101 + @Override
  102 + protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  103 + super.onSizeChanged(w, h, oldw, oldh);
  104 + resetFrameRect(w, h);
  105 + }
  106 +
  107 + private void resetFrameRect(int w, int h) {
  108 + if (shapeType == 1) {
  109 + frameRect.left = (int) (w * 0.05);
  110 + frameRect.top = (int) (h * 0.25);
  111 + } else {
  112 + frameRect.left = (int) (w * 0.2);
  113 + frameRect.top = (int) (h * 0.2);
  114 + }
  115 + frameRect.right = w - frameRect.left;
  116 + frameRect.bottom = h - frameRect.top;
  117 + }
  118 +
  119 + private int shapeType = 0;
  120 +
  121 + public void setTypeWide() {
  122 + shapeType = 1;
  123 + }
  124 +
  125 + private void translate(float x, float y) {
  126 + if (x > 0) {
  127 + // moving left;
  128 + if (frameRect.left - x < margin) {
  129 + x = frameRect.left - margin;
  130 + }
  131 + } else {
  132 + if (frameRect.right - x > getWidth() - margin) {
  133 + x = frameRect.right - getWidth() + margin;
  134 + }
  135 + }
  136 +
  137 + if (y > 0) {
  138 + if (frameRect.top - y < margin) {
  139 + y = frameRect.top - margin;
  140 + }
  141 + } else {
  142 + if (frameRect.bottom - y > getHeight() - margin) {
  143 + y = frameRect.bottom - getHeight() + margin;
  144 + }
  145 + }
  146 + frameRect.offset(-x, -y);
  147 + invalidate();
  148 + }
  149 +
  150 + @Override
  151 + protected void onDraw(Canvas canvas) {
  152 + super.onDraw(canvas);
  153 +
  154 + canvas.drawColor(maskColor);
  155 +
  156 + paint.setStrokeWidth(PxUtil.dp2px(1));
  157 + canvas.drawRect(frameRect, paint);
  158 + canvas.drawRect(frameRect, eraser);
  159 + drawCorners(canvas);
  160 + }
  161 +
  162 + private void drawCorners(Canvas canvas) {
  163 + paint.setStrokeWidth(cornerLineWidth);
  164 + // left top
  165 + drawLine(canvas, frameRect.left - cornerLineWidth / 2, frameRect.top, cornerLength, 0);
  166 + drawLine(canvas, frameRect.left, frameRect.top, 0, cornerLength);
  167 +
  168 + // right top
  169 + drawLine(canvas, frameRect.right + cornerLineWidth / 2, frameRect.top, -cornerLength, 0);
  170 + drawLine(canvas, frameRect.right, frameRect.top, 0, cornerLength);
  171 +
  172 + // right bottom
  173 + drawLine(canvas, frameRect.right, frameRect.bottom, 0, -cornerLength);
  174 + drawLine(canvas, frameRect.right + cornerLineWidth / 2, frameRect.bottom, -cornerLength, 0);
  175 +
  176 + // left bottom
  177 + drawLine(canvas, frameRect.left - cornerLineWidth / 2, frameRect.bottom, cornerLength, 0);
  178 + drawLine(canvas, frameRect.left, frameRect.bottom, 0, -cornerLength);
  179 + }
  180 +
  181 + private void drawLine(Canvas canvas, float x, float y, int dx, int dy) {
  182 + canvas.drawLine(x, y, x + dx, y + dy, paint);
  183 + }
  184 +
  185 + @Override
  186 + public boolean onTouchEvent(MotionEvent event) {
  187 + boolean result = handleDown(event);
  188 + float ex = 60;
  189 + RectF rectExtend = new RectF(frameRect.left - ex, frameRect.top - ex,
  190 + frameRect.right + ex, frameRect.bottom + ex);
  191 + if (!result) {
  192 + if (rectExtend.contains(event.getX(), event.getY())) {
  193 + gestureDetector.onTouchEvent(event);
  194 + return true;
  195 + }
  196 + }
  197 + return result;
  198 + }
  199 +
  200 + private boolean handleDown(MotionEvent event) {
  201 + switch (event.getAction()) {
  202 + case MotionEvent.ACTION_CANCEL:
  203 + case MotionEvent.ACTION_UP:
  204 + currentCorner = -1;
  205 + break;
  206 + case MotionEvent.ACTION_DOWN: {
  207 + float radius = cornerLength;
  208 + touchRect.set(event.getX() - radius, event.getY() - radius, event.getX() + radius,
  209 + event.getY() + radius);
  210 + if (touchRect.contains(frameRect.left, frameRect.top)) {
  211 + currentCorner = CORNER_LEFT_TOP;
  212 + return true;
  213 + }
  214 +
  215 + if (touchRect.contains(frameRect.right, frameRect.top)) {
  216 + currentCorner = CORNER_RIGHT_TOP;
  217 + return true;
  218 + }
  219 +
  220 + if (touchRect.contains(frameRect.right, frameRect.bottom)) {
  221 + currentCorner = CORNER_RIGHT_BOTTOM;
  222 + return true;
  223 + }
  224 +
  225 + if (touchRect.contains(frameRect.left, frameRect.bottom)) {
  226 + currentCorner = CORNER_LEFT_BOTTOM;
  227 + return true;
  228 + }
  229 + return false;
  230 + }
  231 + case MotionEvent.ACTION_MOVE:
  232 + return handleScale(event);
  233 + default:
  234 +
  235 + }
  236 + return false;
  237 + }
  238 +
  239 + private boolean handleScale(MotionEvent event) {
  240 + switch (currentCorner) {
  241 + case CORNER_LEFT_TOP:
  242 + scaleTo(event.getX(), event.getY(), frameRect.right, frameRect.bottom);
  243 + return true;
  244 + case CORNER_RIGHT_TOP:
  245 + scaleTo(frameRect.left, event.getY(), event.getX(), frameRect.bottom);
  246 + return true;
  247 + case CORNER_RIGHT_BOTTOM:
  248 + scaleTo(frameRect.left, frameRect.top, event.getX(), event.getY());
  249 + return true;
  250 + case CORNER_LEFT_BOTTOM:
  251 + scaleTo(event.getX(), frameRect.top, frameRect.right, event.getY());
  252 + return true;
  253 + default:
  254 + return false;
  255 + }
  256 + }
  257 +
  258 + private void scaleTo(float left, float top, float right, float bottom) {
  259 + if (bottom - top < getMinimumFrameHeight()) {
  260 + top = frameRect.top;
  261 + bottom = frameRect.bottom;
  262 + }
  263 + if (right - left < getMinimumFrameWidth()) {
  264 + left = frameRect.left;
  265 + right = frameRect.right;
  266 + }
  267 + left = Math.max(margin, left);
  268 + top = Math.max(margin, top);
  269 + right = Math.min(getWidth() - margin, right);
  270 + bottom = Math.min(getHeight() - margin, bottom);
  271 +
  272 + frameRect.set(left, top, right, bottom);
  273 + invalidate();
  274 + }
  275 +
  276 +
  277 + private void notifyFrameChange() {
  278 + if (onFrameChangeListener != null) {
  279 + onFrameChangeListener.onFrameChange(frameRect);
  280 + }
  281 + }
  282 +
  283 + private float getMinimumFrameWidth() {
  284 + return 2.4f * cornerLength;
  285 + }
  286 +
  287 + private float getMinimumFrameHeight() {
  288 + return 2.4f * cornerLength;
  289 + }
  290 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/util/ImageUtil.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.cnlive.core.ocr.ui.util;
  5 +
  6 +import android.content.Context;
  7 +import android.graphics.Bitmap;
  8 +import android.graphics.BitmapFactory;
  9 +import android.graphics.Canvas;
  10 +import android.graphics.Paint;
  11 +import android.graphics.Path;
  12 +import android.graphics.Rect;
  13 +import android.media.ExifInterface;
  14 +import android.util.Log;
  15 +
  16 +public class ImageUtil {
  17 + private static final String TAG = "CameraExif";
  18 +
  19 + public static int exifToDegrees(int exifOrientation) {
  20 + if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
  21 + return 90;
  22 + } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
  23 + return 180;
  24 + } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
  25 + return 270;
  26 + }
  27 + return 0;
  28 + }
  29 +
  30 + // Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
  31 + public static int getOrientation(byte[] jpeg) {
  32 + if (jpeg == null) {
  33 + return 0;
  34 + }
  35 +
  36 + int offset = 0;
  37 + int length = 0;
  38 +
  39 + // ISO/IEC 10918-1:1993(E)
  40 + while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
  41 + int marker = jpeg[offset] & 0xFF;
  42 +
  43 + // Check if the marker is a padding.
  44 + if (marker == 0xFF) {
  45 + continue;
  46 + }
  47 + offset++;
  48 +
  49 + // Check if the marker is SOI or TEM.
  50 + if (marker == 0xD8 || marker == 0x01) {
  51 + continue;
  52 + }
  53 + // Check if the marker is EOI or SOS.
  54 + if (marker == 0xD9 || marker == 0xDA) {
  55 + break;
  56 + }
  57 +
  58 + // Get the length and check if it is reasonable.
  59 + length = pack(jpeg, offset, 2, false);
  60 + if (length < 2 || offset + length > jpeg.length) {
  61 + Log.e(TAG, "Invalid length");
  62 + return 0;
  63 + }
  64 +
  65 + // Break if the marker is EXIF in APP1.
  66 + if (marker == 0xE1 && length >= 8
  67 + && pack(jpeg, offset + 2, 4, false) == 0x45786966
  68 + && pack(jpeg, offset + 6, 2, false) == 0) {
  69 + offset += 8;
  70 + length -= 8;
  71 + break;
  72 + }
  73 +
  74 + // Skip other markers.
  75 + offset += length;
  76 + length = 0;
  77 + }
  78 +
  79 + // JEITA CP-3451 Exif Version 2.2
  80 + if (length > 8) {
  81 + // Identify the byte order.
  82 + int tag = pack(jpeg, offset, 4, false);
  83 + if (tag != 0x49492A00 && tag != 0x4D4D002A) {
  84 + Log.e(TAG, "Invalid byte order");
  85 + return 0;
  86 + }
  87 + boolean littleEndian = (tag == 0x49492A00);
  88 +
  89 + // Get the offset and check if it is reasonable.
  90 + int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
  91 + if (count < 10 || count > length) {
  92 + Log.e(TAG, "Invalid offset");
  93 + return 0;
  94 + }
  95 + offset += count;
  96 + length -= count;
  97 +
  98 + // Get the count and go through all the elements.
  99 + count = pack(jpeg, offset - 2, 2, littleEndian);
  100 + while (count-- > 0 && length >= 12) {
  101 + // Get the tag and check if it is orientation.
  102 + tag = pack(jpeg, offset, 2, littleEndian);
  103 + if (tag == 0x0112) {
  104 + // We do not really care about type and count, do we?
  105 + int orientation = pack(jpeg, offset + 8, 2, littleEndian);
  106 + switch (orientation) {
  107 + case 1:
  108 + return 0;
  109 + case 3:
  110 + return 180;
  111 + case 6:
  112 + return 90;
  113 + case 8:
  114 + return 270;
  115 + default:
  116 + return 0;
  117 + }
  118 + }
  119 + offset += 12;
  120 + length -= 12;
  121 + }
  122 + }
  123 +
  124 + Log.i(TAG, "Orientation not found");
  125 + return 0;
  126 + }
  127 +
  128 + private static int pack(byte[] bytes, int offset, int length,
  129 + boolean littleEndian) {
  130 + int step = 1;
  131 + if (littleEndian) {
  132 + offset += length - 1;
  133 + step = -1;
  134 + }
  135 +
  136 + int value = 0;
  137 + while (length-- > 0) {
  138 + value = (value << 8) | (bytes[offset] & 0xFF);
  139 + offset += step;
  140 + }
  141 + return value;
  142 + }
  143 +
  144 +
  145 + public static int calculateInSampleSize(
  146 + BitmapFactory.Options options, int reqWidth, int reqHeight) {
  147 + // Raw height and width of image
  148 + final int height = options.outHeight;
  149 + final int width = options.outWidth;
  150 + int inSampleSize = 1;
  151 +
  152 + if (height > reqHeight || width > reqWidth) {
  153 +
  154 + final int halfHeight = height / 2;
  155 + final int halfWidth = width / 2;
  156 + // Calculate the largest inSampleSize value that is a power of 2 and keeps both
  157 + // height and width larger than the requested height and width.
  158 + while ((halfHeight / inSampleSize) >= reqHeight
  159 + && (halfWidth / inSampleSize) >= reqWidth) {
  160 + inSampleSize *= 2;
  161 + }
  162 + }
  163 +
  164 + return inSampleSize;
  165 + }
  166 +
  167 + /**
  168 + * 绘制文字到右下角
  169 + *
  170 + * @param context
  171 + * @param bitmap
  172 + * @param text
  173 + * @param size
  174 + * @param color
  175 + * @param paddingRight
  176 + * @param paddingBottom
  177 + * @return
  178 + */
  179 + public static Bitmap drawTextToRightBottom(Context context, Bitmap bitmap, String text,
  180 + int size, int color, int paddingRight, int paddingBottom) {
  181 + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
  182 + paint.setColor(color);
  183 + paint.setTextSize(dp2px(context, size));
  184 + Rect bounds = new Rect();
  185 + paint.getTextBounds(text, 0, text.length(), bounds);
  186 + return drawTextToBitmap(context, bitmap, text, paint, bounds,
  187 + bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight),
  188 + bitmap.getHeight() - dp2px(context, paddingBottom));
  189 + }
  190 +
  191 + //图片上绘制文字
  192 + private static Bitmap drawTextToBitmap(Context context, Bitmap bitmap, String text,
  193 + Paint paint, Rect bounds, int paddingLeft, int paddingTop) {
  194 + Bitmap.Config bitmapConfig = bitmap.getConfig();
  195 +
  196 + paint.setDither(true); // 获取跟清晰的图像采样
  197 + paint.setFilterBitmap(true);// 过滤一些
  198 + if (bitmapConfig == null) {
  199 + bitmapConfig = Bitmap.Config.ARGB_8888;
  200 + }
  201 + bitmap = bitmap.copy(bitmapConfig, true);
  202 + Canvas canvas = new Canvas(bitmap);
  203 +// canvas.rotate(-45);
  204 +// canvas.drawText(text, paddingLeft, paddingTop, paint);
  205 +
  206 + Path path = new Path();
  207 + path.moveTo(bitmap.getWidth() / 2, bitmap.getHeight());
  208 + path.lineTo(bitmap.getWidth(), 50);
  209 +
  210 + canvas.drawTextOnPath(text, path, 20, 20, paint);
  211 +
  212 + return bitmap;
  213 + }
  214 +
  215 + /**
  216 + * dip转pix
  217 + *
  218 + * @param context
  219 + * @param dp
  220 + * @return
  221 + */
  222 + public static int dp2px(Context context, float dp) {
  223 + final float scale = context.getResources().getDisplayMetrics().density;
  224 + return (int) (dp * scale + 0.5f);
  225 + }
  226 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/ocr/ui/util/PxUtil.java 0 → 100644
  1 +package com.cnlive.core.ocr.ui.util;
  2 +
  3 +import android.content.res.Resources;
  4 +
  5 +public class PxUtil {
  6 +
  7 + public static int dp2px(float dpValue) {
  8 + return (int) (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);
  9 + }
  10 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/people_verfication/TakePictureActivity.java deleted 100644 → 0
1   -package com.cnlive.core.people_verfication;
2   -
3   -
4   -import com.cnlive.core.libs.base.frame.activity.BaseActivity;
5   -
6   -/**
7   - * 拍摄照片 身份证件采集
8   - */
9   -public class TakePictureActivity extends BaseActivity {
10   -
11   - @Override
12   - protected int getViewLayoutId() {
13   - return R.layout.activity_take_picture;
14   - }
15   -
16   - @Override
17   - protected void initView() {
18   -
19   - }
20   -}
core/dentity_verfication/src/main/java/com/cnlive/core/people_verfication/activity/TakeIdentityPictureActivity.java 0 → 100644
  1 +package com.cnlive.core.people_verfication.activity;
  2 +
  3 +
  4 +import android.Manifest;
  5 +import android.app.Activity;
  6 +import android.content.Intent;
  7 +import android.text.TextUtils;
  8 +
  9 +import com.baidu.ocr.sdk.model.IDCardParams;
  10 +import com.cnlive.core.libs.base.frame.activity.BaseActivity;
  11 +import com.cnlive.core.libs.base.util.FileUtils;
  12 +import com.cnlive.core.ocr.ui.camera.CameraActivity;
  13 +import com.cnlive.core.people_verfication.R;
  14 +import com.cnlive.core.people_verfication.databinding.ActivityTakePictureBinding;
  15 +import com.cnlive.core.people_verfication.presenter.TakeIdentityPicturePresenter;
  16 +import com.cnlive.core.people_verfication.view.ICertificatePhohosView;
  17 +import com.cnlive.core.people_verfication.view.TakeIdentityPictureView;
  18 +import com.cnlive.media.picker.utils.PermissionChecker;
  19 +
  20 +import androidx.annotation.NonNull;
  21 +import androidx.databinding.ViewDataBinding;
  22 +
  23 +/**
  24 + * 拍摄照片 身份证件采集
  25 + */
  26 +public class TakeIdentityPictureActivity extends BaseActivity<TakeIdentityPictureView, TakeIdentityPicturePresenter> {
  27 +
  28 + public ActivityTakePictureBinding binding;
  29 + //上一个被点击过的btn的点击时间
  30 + private long preViewClickTime = 0;
  31 + //上一个被点击过的btn的id
  32 + private int preViewClickId = 0;
  33 + private static int CAMERA_WRITE_FR_PERMISSION = 1001;
  34 + private static int CAMERA_WRITE_BA_PERMISSION = 1002;
  35 +
  36 + @Override
  37 + protected int getBindLayoutId() {
  38 + return R.layout.activity_take_picture;
  39 + }
  40 +
  41 + @Override
  42 + protected void initBindingLayoutData(ViewDataBinding baseBinding) {
  43 + binding = (ActivityTakePictureBinding) baseBinding;
  44 + }
  45 +
  46 + @Override
  47 + protected void initView() {
  48 + binding.topbar.setTitle("拍摄照片");
  49 + binding.topbar.setLeft(R.drawable.back);
  50 + binding.topbar.addLeftImageButton(R.drawable.top_back_black, R.id.fragment_back).setOnClickListener(v -> {
  51 + finish();
  52 + });
  53 + binding.setClick(v -> onClick(v.getId()));
  54 + }
  55 +
  56 + private void onClick(int id) {
  57 + long curClickTime = System.currentTimeMillis();
  58 + if (preViewClickId > 0 && preViewClickTime > 0) {
  59 + if (preViewClickId == id && curClickTime - preViewClickTime <= 2000) {
  60 + return;
  61 + }
  62 + }
  63 + preViewClickTime = curClickTime;
  64 + preViewClickId = id;
  65 + if (id == R.id.certificate_front) {
  66 + if(hasCampermission(CAMERA_WRITE_FR_PERMISSION)){//权限判断
  67 + getMvpView().takeIdCardPictures(this, ICertificatePhohosView.ID_CARD_SIDE_FRONT);
  68 + }
  69 + } else if (id == R.id.certificate_back) {
  70 + if(hasCampermission(CAMERA_WRITE_BA_PERMISSION)){//权限判断
  71 + getMvpView().takeIdCardPictures(this, ICertificatePhohosView.ID_CARD_SIDE_BACK);
  72 + }
  73 + } else if (id == R.id.take_picture) {
  74 +
  75 + }
  76 + }
  77 +
  78 + private boolean hasCampermission(int typeNum){
  79 + boolean hasPermission = PermissionChecker.checkPermissions(this
  80 + , new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}
  81 + , typeNum, R.string.dialog_imagepicker_permission_camera_nerver_ask_message);
  82 + return hasPermission;
  83 + }
  84 +
  85 + @Override
  86 + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  87 + super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  88 + if (requestCode == CAMERA_WRITE_FR_PERMISSION) {
  89 + if (PermissionChecker.onRequestPermissionsResult(this,requestCode, permissions, grantResults, false, R.string.dialog_imagepicker_permission_sdcard_message)[0]) {
  90 + getMvpView().takeIdCardPictures(this, ICertificatePhohosView.ID_CARD_SIDE_FRONT);
  91 + }
  92 + }else if(requestCode == CAMERA_WRITE_BA_PERMISSION){
  93 + if (PermissionChecker.onRequestPermissionsResult(this,requestCode, permissions, grantResults, false, R.string.dialog_imagepicker_permission_sdcard_message)[0]) {
  94 + getMvpView().takeIdCardPictures(this, ICertificatePhohosView.ID_CARD_SIDE_BACK);
  95 + }
  96 + }
  97 + }
  98 +
  99 + @Override
  100 + protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  101 + super.onActivityResult(requestCode, resultCode, data);
  102 + if (requestCode == ICertificatePhohosView.REQUEST_CODE_CAMERA && resultCode == Activity.RESULT_OK) {
  103 + if (data != null) {
  104 + String contentType = data.getStringExtra(CameraActivity.KEY_CONTENT_TYPE);
  105 + if (!TextUtils.isEmpty(contentType)) {
  106 + if (CameraActivity.CONTENT_TYPE_ID_CARD_FRONT.equals(contentType)) {
  107 + String frontFilePath = FileUtils.getSaveFile(getApplicationContext(), ICertificatePhohosView.PIC_FRONT).getAbsolutePath();
  108 + if (getMvpView() == null) return;
  109 + getMvpView().recIDCard(IDCardParams.ID_CARD_SIDE_FRONT, frontFilePath);
  110 +
  111 + } else if (CameraActivity.CONTENT_TYPE_ID_CARD_BACK.equals(contentType)) {
  112 + String backFilePath = FileUtils.getSaveFile(getApplicationContext(), ICertificatePhohosView.PIC_BACK).getAbsolutePath();
  113 + if (getMvpView() == null) return;
  114 + getMvpView().recIDCard(IDCardParams.ID_CARD_SIDE_BACK, backFilePath);
  115 + }
  116 + }
  117 + }
  118 + }
  119 + }
  120 +
  121 + @Override
  122 + protected void onDestroy() {
  123 + super.onDestroy();
  124 +// if (getPresenter() != null) getPresenter().cancel();
  125 + }
  126 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/people_verfication/presenter/TakeIdentityPicturePresenter.java 0 → 100644
  1 +package com.cnlive.core.people_verfication.presenter;
  2 +
  3 +import com.cnlive.core.libs.base.frame.presenter.BasePresenter;
  4 +import com.cnlive.core.people_verfication.view.TakeIdentityPictureView;
  5 +
  6 +public class TakeIdentityPicturePresenter extends BasePresenter<TakeIdentityPictureView> {
  7 +
  8 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/people_verfication/view/ICertificatePhohosView.java 0 → 100644
  1 +package com.cnlive.core.people_verfication.view;
  2 +
  3 +import com.hannesdorfmann.mosby3.mvp.MvpView;
  4 +
  5 +/**
  6 + * Created by cnlive20080010 on 2018/10/9.
  7 + */
  8 +
  9 +public interface ICertificatePhohosView extends MvpView {
  10 + int REQUEST_CODE_CAMERA = 102;
  11 + int ID_CARD_SIDE_FRONT = 1;
  12 + int ID_CARD_SIDE_BACK = 2;
  13 + String PIC_FRONT = "pic_front";
  14 + String PIC_BACK = "pic_back";
  15 +}
... ...
core/dentity_verfication/src/main/java/com/cnlive/core/people_verfication/view/TakeIdentityPictureView.java 0 → 100644
  1 +package com.cnlive.core.people_verfication.view;
  2 +
  3 +import android.app.Activity;
  4 +import android.content.Context;
  5 +import android.content.Intent;
  6 +import android.graphics.Bitmap;
  7 +import android.graphics.BitmapFactory;
  8 +import android.hardware.Camera;
  9 +import android.os.Environment;
  10 +import android.os.Handler;
  11 +import android.os.Looper;
  12 +import android.text.TextUtils;
  13 +import android.widget.ImageView;
  14 +
  15 +import com.baidu.ocr.sdk.OCR;
  16 +import com.baidu.ocr.sdk.OnResultListener;
  17 +import com.baidu.ocr.sdk.exception.OCRError;
  18 +import com.baidu.ocr.sdk.model.AccessToken;
  19 +import com.baidu.ocr.sdk.model.IDCardParams;
  20 +import com.baidu.ocr.sdk.model.IDCardResult;
  21 +import com.bumptech.glide.load.engine.DiskCacheStrategy;
  22 +import com.bumptech.glide.request.RequestOptions;
  23 +import com.cnlive.core.libs.base.application.AppConfig;
  24 +import com.cnlive.core.libs.base.frame.view.BaseView;
  25 +import com.cnlive.core.libs.base.util.AlertUtil;
  26 +import com.cnlive.core.libs.base.util.FileUtils;
  27 +import com.cnlive.core.libs.base.util.GlideUtil;
  28 +import com.cnlive.core.ocr.RecognizeService;
  29 +import com.cnlive.core.ocr.ui.camera.CameraActivity;
  30 +import com.cnlive.core.ocr.ui.util.ImageUtil;
  31 +import com.cnlive.core.people_verfication.R;
  32 +import com.cnlive.core.people_verfication.activity.TakeIdentityPictureActivity;
  33 +
  34 +import java.io.ByteArrayOutputStream;
  35 +import java.io.File;
  36 +import java.io.FileNotFoundException;
  37 +import java.io.FileOutputStream;
  38 +import java.io.IOException;
  39 +import java.text.SimpleDateFormat;
  40 +import java.util.Date;
  41 +
  42 +import static com.cnlive.core.people_verfication.view.ICertificatePhohosView.PIC_BACK;
  43 +import static com.cnlive.core.people_verfication.view.ICertificatePhohosView.PIC_FRONT;
  44 +
  45 +public class TakeIdentityPictureView extends BaseView {
  46 +
  47 + public TakeIdentityPictureActivity activity;
  48 + //上一个被点击过的btn的点击时间
  49 + private long preViewClickTime = 0;
  50 + private boolean isFrontEffective = false;
  51 + private boolean isBackEffective = false;
  52 + private String idCardNo;
  53 + private String realName;
  54 +
  55 + public TakeIdentityPictureView(TakeIdentityPictureActivity activity) {
  56 + this.activity = activity;
  57 + }
  58 +
  59 + private Activity getContext() {
  60 + return activity == null ? null : activity;
  61 + }
  62 +
  63 + public void finish() {
  64 + activity.finish();
  65 + }
  66 +
  67 + /**
  68 + * 拍摄
  69 + *
  70 + * @param idCardSide
  71 + */
  72 + public void takeIdCardPictures(Context context, final int idCardSide) {
  73 + long curClickTime = System.currentTimeMillis();
  74 + if (preViewClickTime > 0 && curClickTime - preViewClickTime <= 2000 ) {
  75 + return;
  76 + }
  77 + preViewClickTime = curClickTime;
  78 + OCR.getInstance().initAccessTokenWithAkSk(new OnResultListener<AccessToken>() {
  79 + @Override
  80 + public void onResult(AccessToken result) {
  81 + String token = result.getAccessToken();
  82 + if (idCardSide == ICertificatePhohosView.ID_CARD_SIDE_FRONT) {
  83 + takePhoto(PIC_FRONT, CameraActivity.CONTENT_TYPE_ID_CARD_FRONT);
  84 + } else if (idCardSide == ICertificatePhohosView.ID_CARD_SIDE_BACK) {
  85 + takePhoto(PIC_BACK, CameraActivity.CONTENT_TYPE_ID_CARD_BACK);
  86 + }
  87 + }
  88 +
  89 + @Override
  90 + public void onError(OCRError error) {
  91 + error.printStackTrace();
  92 + new Thread() {
  93 + @Override
  94 + public void run() {
  95 + new Handler(Looper.getMainLooper()).post(new Runnable() {
  96 + @Override
  97 + public void run() {
  98 + AlertUtil.showDeftToast(getContext(), getContext().getString(R.string.init_baidu_faile));
  99 + }
  100 + });
  101 + }
  102 + }.start();
  103 + }
  104 + }, context, AppConfig.getBaiduAppKey(), AppConfig.getBaiduAppSecret());
  105 + }
  106 +
  107 + /**
  108 + * 拍摄身份证
  109 + *
  110 + * @param fileName
  111 + * @param contentType
  112 + */
  113 + public void takePhoto(String fileName, String contentType) {
  114 + Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
  115 + Camera camera = null;
  116 + int cameraId = 0;
  117 + for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
  118 + Camera.getCameraInfo(i, cameraInfo);
  119 + if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
  120 + cameraId = i;
  121 + }
  122 + }
  123 + try {
  124 + camera = Camera.open(cameraId);
  125 + Intent intent = new Intent(getContext(), CameraActivity.class);
  126 + intent.putExtra(CameraActivity.KEY_OUTPUT_FILE_PATH,
  127 + FileUtils.getSaveFile(getContext().getApplication(), fileName).getAbsolutePath());
  128 + intent.putExtra(CameraActivity.KEY_CONTENT_TYPE, contentType);
  129 + getContext().startActivityForResult(intent, ICertificatePhohosView.REQUEST_CODE_CAMERA);
  130 + camera.release();
  131 + } catch (Throwable e) {
  132 + e.printStackTrace();
  133 + AlertUtil.showDeftToast(getContext(),"没有相机权限,相机开启失败");
  134 + return;
  135 + }
  136 + }
  137 +
  138 + /**
  139 + * 展示拍摄的身份证,并获取身份证信息
  140 + *
  141 + * @param idCardSide
  142 + * @param bitmap
  143 + * @param imageView
  144 + */
  145 + public void showIdCard(String idCardSide, Bitmap bitmap, ImageView imageView) {
  146 + RequestOptions options = new RequestOptions();
  147 + options.skipMemoryCache(true);
  148 + options.diskCacheStrategy(DiskCacheStrategy.NONE);
  149 + GlideUtil.into(imageView, bitmap, options);
  150 + }
  151 +
  152 + /**
  153 + * 身份证人像面信息是否完整
  154 + *
  155 + * @param idCardResult
  156 + * @return
  157 + */
  158 + private boolean isEffectiveFront(IDCardResult idCardResult) {
  159 + return idCardResult.getName() != null && idCardResult.getAddress() != null && idCardResult.getIdNumber() != null && idCardResult.getBirthday() != null && idCardResult.getGender() != null && idCardResult.getEthnic() != null;
  160 + }
  161 +
  162 + /**
  163 + * 身份证国徽面信息是否完整
  164 + *
  165 + * @param idCardResult
  166 + * @return
  167 + */
  168 + private boolean isEffectiveBack(IDCardResult idCardResult) {
  169 + return idCardResult.getSignDate() != null && idCardResult.getExpiryDate() != null && idCardResult.getIssueAuthority() != null;
  170 + }
  171 +
  172 + private Bitmap frontBitmap;
  173 + private Bitmap backBitmap;
  174 +
  175 + /**
  176 + * 验证身份证信息
  177 + *
  178 + * @param idCardSide
  179 + * @param filePath
  180 + */
  181 + public void recIDCard(final String idCardSide, final String filePath) {
  182 + RecognizeService.recIDCard(idCardSide, filePath, new OnResultListener<IDCardResult>() {
  183 + @Override
  184 + public void onResult(IDCardResult idCardResult) {
  185 + if (idCardResult != null) {
  186 + if (isEffectiveFront(idCardResult) || isEffectiveBack(idCardResult)) {
  187 + Bitmap bitmap = ImageUtil.drawTextToRightBottom(getContext(), BitmapFactory.decodeFile(filePath), getContext().getString(R.string.only_for_verified), 20, getContext().getResources().getColor(R.color.grayText), 10, 10);
  188 +
  189 + if (idCardSide.equals(IDCardParams.ID_CARD_SIDE_FRONT)) {
  190 + isFrontEffective = true;
  191 + idCardNo = idCardResult.getIdNumber().toString();
  192 + realName = idCardResult.getName().toString();
  193 + if (TextUtils.isEmpty(idCardNo) || TextUtils.isEmpty(realName)) {
  194 +// activity.binding.submit.setClickable(false);
  195 + AlertUtil.showDeftToast(getContext(), "身份证信息读取失败,请重新拍摄");
  196 + return;
  197 + }
  198 + frontBitmap = bitmap;
  199 + showIdCard(idCardSide, frontBitmap, activity.binding.certificateFront);
  200 + activity.binding.certificateFront.setImageBitmap(bitmap);
  201 + if(isBackEffective){
  202 +// activity.binding.submit.setClickable(true);
  203 +// activity.binding.submit.setTextColor(activity.getResources().getColor(R.color.white));
  204 +// activity.binding.submit.setBackground(getContext().getResources().getDrawable(R.drawable.bg_apply_btn));
  205 + }
  206 + }
  207 +
  208 + if (idCardSide.equals(IDCardParams.ID_CARD_SIDE_BACK)) {
  209 + backBitmap = bitmap;
  210 + isBackEffective = true;
  211 + if(TextUtils.isEmpty(idCardResult.getIssueAuthority().toString().trim()) ||
  212 + TextUtils.isEmpty(idCardResult.getSignDate().toString().trim())){
  213 +// activity.binding.submit.setClickable(false);
  214 + AlertUtil.showDeftToast(getContext(), "身份证信息读取失败,请重新拍摄");
  215 + return;
  216 + }
  217 + try{
  218 + Integer.parseInt(idCardResult.getExpiryDate().toString().trim());
  219 + if(idCardResult.getExpiryDate() != null && Integer.parseInt(idCardResult.getExpiryDate().toString().trim()) < getCurrentTime()){
  220 +// activity.binding.submit.setClickable(false);
  221 + AlertUtil.showDeftToast(getContext(), "身份证有效期失效");
  222 + return;
  223 + }
  224 + }catch (Exception e){
  225 + e.printStackTrace();
  226 + }
  227 +
  228 +
  229 + showIdCard(idCardSide, backBitmap, activity.binding.certificateBack);
  230 + activity.binding.certificateBack.setImageBitmap(bitmap);
  231 + if (!TextUtils.isEmpty(idCardNo) && !TextUtils.isEmpty(realName)) {
  232 +// activity.binding.submit.setClickable(true);
  233 +// activity.binding.submit.setTextColor(activity.getResources().getColor(R.color.white));
  234 +// activity.binding.submit.setBackground(getContext().getResources().getDrawable(R.drawable.bg_apply_btn));
  235 + }
  236 + }
  237 + }else {
  238 +// activity.binding.submit.setClickable(false);
  239 + AlertUtil.showDeftToast(getContext(), "身份证信息读取失败,请重新拍摄");
  240 + }
  241 + }
  242 + }
  243 +
  244 + @Override
  245 + public void onError(OCRError ocrError) {
  246 + if (ocrError.getErrorCode() == 283504) {
  247 + AlertUtil.showDeftToast(getContext(), "网络请求失败,请稍后再试");
  248 + } else {
  249 + AlertUtil.showDeftToast(getContext(), "识别失败,请重拍");
  250 + }
  251 + }
  252 + });
  253 +
  254 + }
  255 +
  256 + private Integer getCurrentTime() {
  257 + SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMdd");
  258 + String date = sDateFormat.format(new Date());
  259 + return Integer.parseInt(date);
  260 + }
  261 +
  262 + public File compressImage(Bitmap bitmap, String filename) {
  263 + ByteArrayOutputStream baos = new ByteArrayOutputStream();
  264 + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
  265 + int options = 100;
  266 + while (baos.toByteArray().length / 1024 > 500) { //循环判断如果压缩后图片是否大于500kb,大于继续压缩
  267 + baos.reset();//重置baos即清空baos
  268 + options -= 10;//每次都减少10
  269 + bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
  270 + long length = baos.toByteArray().length;
  271 + }
  272 + File file = new File(Environment.getExternalStorageDirectory() + "/aegis/file");
  273 + if (!file.exists()) {
  274 + file.mkdirs();
  275 + }
  276 + file = new File(Environment.getExternalStorageDirectory() + "/aegis/file/", filename + ".png");
  277 + try {
  278 + FileOutputStream fos = new FileOutputStream(file);
  279 + try {
  280 + fos.write(baos.toByteArray());
  281 + fos.flush();
  282 + fos.close();
  283 + } catch (IOException e) {
  284 + e.printStackTrace();
  285 + }
  286 + } catch (FileNotFoundException e) {
  287 + e.printStackTrace();
  288 + }
  289 +// recycleBitmap(bitmap);
  290 + return file;
  291 + }
  292 +}
... ...
core/dentity_verfication/src/main/jniLibs/arm64-v8a/libidcard_quality.1.1.1.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/arm64-v8a/libidl_license.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/arm64-v8a/libocr-sdk.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/armeabi-v7a/libidcard_quality.1.1.1.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/armeabi-v7a/libidl_license.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/armeabi-v7a/libocr-sdk.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/armeabi/libidcard_quality.1.1.1.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/armeabi/libidl_license.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/armeabi/libocr-sdk.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/x86/libidcard_quality.1.1.1.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/x86/libidl_license.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/jniLibs/x86/libocr-sdk.so 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_cancel.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_close.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_confirm.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_gallery.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_hint_align_bank_card.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_hint_align_id_card.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_hint_align_id_card_back.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_id_card_locator_back.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_id_card_locator_front.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_light_off.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_light_on.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_reset.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_rotate.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_round_corner.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<shape xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <solid android:color="#000000" />
  4 + <corners android:radius="12dp" />
  5 +</shape>
0 6 \ No newline at end of file
... ...
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_take_photo_highlight.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_take_photo_normal.webp 0 → 100644
No preview for this file type
core/dentity_verfication/src/main/res/drawable-xhdpi/bd_ocr_take_photo_selector.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<selector xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <!-- 按压时 -->
  4 + <item android:drawable="@drawable/bd_ocr_take_photo_highlight" android:state_pressed="true" />
  5 + <!-- 被选中时 -->
  6 + <item android:drawable="@drawable/bd_ocr_take_photo_normal" android:state_selected="true" />
  7 + <!-- 被激活时 -->
  8 + <item android:drawable="@drawable/bd_ocr_take_photo_normal" android:state_activated="true" />
  9 + <!-- 默认时 -->
  10 + <item android:drawable="@drawable/bd_ocr_take_photo_normal" />
  11 +</selector>
0 12 \ No newline at end of file
... ...