Browse Source

授权、sdk初始化、读取身份证,相机预览、人证比对功能、启动页、登录页、自主登录页、访客身份证登记页

ifengouy 2 years ago
parent
commit
5d950c4f10
72 changed files with 4848 additions and 69 deletions
  1. 25 0
      app/build.gradle
  2. BIN
      app/libs/FacePassAndroidSDK-McvSafe-release.aar
  3. BIN
      app/libs/armeabi-v7a/libwlt2bmp.so
  4. BIN
      app/libs/armeabi-v7a/libzkwltdecode.so
  5. BIN
      app/libs/mcvSafe.v1.2.2.aar
  6. BIN
      app/libs/universal-image-loader-1.9.3.jar
  7. BIN
      app/libs/zkandroidcore.jar
  8. BIN
      app/libs/zkandroididcardreader.jar
  9. 0 7
      app/src/main/AndroidManifest.xml
  10. 1 0
      app/src/main/assets/CBG_Android_Face_Reco---36502-Formal-one-stage.cert
  11. BIN
      app/src/main/assets/attr.RC.gray.12M.arm.200229.bin
  12. BIN
      app/src/main/assets/attr.age_gender.surveillance.nnie.av200.0.1.0.190630.bin
  13. BIN
      app/src/main/assets/attr.pose_blur.align.av200.190630.bin
  14. BIN
      app/src/main/assets/attr.smile.mgf29.0.1.1.181229.bin
  15. BIN
      app/src/main/assets/detector.arm.E.bin
  16. BIN
      app/src/main/assets/detector_rect.arm.E.bin
  17. BIN
      app/src/main/assets/feat2.arm.H.v1.0_1core.bin
  18. BIN
      app/src/main/assets/fonts/NeogreyRegular.otf
  19. BIN
      app/src/main/assets/fonts/Univers Condensed.ttf
  20. BIN
      app/src/main/assets/fonts/Univers LT 57 Condensed.ttf
  21. BIN
      app/src/main/assets/fonts/hua.ttf
  22. BIN
      app/src/main/assets/fonts/kai.ttf
  23. BIN
      app/src/main/assets/fonts/spyagency3.ttf
  24. BIN
      app/src/main/assets/liveness.CPU.rgb.int8.E.bin
  25. BIN
      app/src/main/assets/liveness.CPU.rgbir.int8.E.bin
  26. BIN
      app/src/main/assets/liveness.GPU.AlgoPolicy.E.cache
  27. BIN
      app/src/main/assets/liveness.GPU.rgb.E.bin
  28. BIN
      app/src/main/assets/liveness.GPU.rgbir.E.bin
  29. BIN
      app/src/main/assets/occlusion.all_attr_configurable.occ.190816.bin
  30. BIN
      app/src/main/assets/pf.lmk.arm.D.bin
  31. 120 7
      app/src/main/java/com/sunwin/visitorapp/SplashActivity.java
  32. 116 8
      app/src/main/java/com/sunwin/visitorapp/activity/IdcardRegActivity.java
  33. 2 0
      app/src/main/java/com/sunwin/visitorapp/activity/VisitorRegActivity.java
  34. 69 0
      app/src/main/java/com/sunwin/visitorapp/adapter/CardAdapter.java
  35. 222 0
      app/src/main/java/com/sunwin/visitorapp/camera/CameraHolder.java
  36. 218 0
      app/src/main/java/com/sunwin/visitorapp/camera/CameraSurfaceView.java
  37. 22 0
      app/src/main/java/com/sunwin/visitorapp/face/CameraPreviewData.java
  38. 22 0
      app/src/main/java/com/sunwin/visitorapp/face/DetectResult.java
  39. 368 0
      app/src/main/java/com/sunwin/visitorapp/face/FRAbsLoop.java
  40. 22 0
      app/src/main/java/com/sunwin/visitorapp/face/FRAbsLoopFactory.java
  41. 12 0
      app/src/main/java/com/sunwin/visitorapp/face/FaceDecterFactory.java
  42. 196 0
      app/src/main/java/com/sunwin/visitorapp/face/IDCardReaderUtil.java
  43. 37 0
      app/src/main/java/com/sunwin/visitorapp/face/IFaceInfo.java
  44. 7 0
      app/src/main/java/com/sunwin/visitorapp/face/IdCardReadCallback.java
  45. 515 0
      app/src/main/java/com/sunwin/visitorapp/face/SwFaceLoop.java
  46. 19 0
      app/src/main/java/com/sunwin/visitorapp/model/BaseModel.java
  47. 12 0
      app/src/main/java/com/sunwin/visitorapp/model/FacesModel.java
  48. 69 0
      app/src/main/java/com/sunwin/visitorapp/model/NetResultInfo.java
  49. 147 0
      app/src/main/java/com/sunwin/visitorapp/model/OpenDoorByFaceNetResultInfo.java
  50. 4 0
      app/src/main/java/com/sunwin/visitorapp/model/UserModel.java
  51. 37 0
      app/src/main/java/com/sunwin/visitorapp/model/bus/DescEvent.java
  52. 9 0
      app/src/main/java/com/sunwin/visitorapp/utils/AppUtil.java
  53. 1596 0
      app/src/main/java/com/sunwin/visitorapp/utils/BitmapUtils.java
  54. 74 0
      app/src/main/java/com/sunwin/visitorapp/utils/Constant.java
  55. 0 30
      app/src/main/java/com/sunwin/visitorapp/utils/Constants.java
  56. 236 0
      app/src/main/java/com/sunwin/visitorapp/utils/ConvertUtil.java
  57. 38 0
      app/src/main/java/com/sunwin/visitorapp/utils/FileUtil.java
  58. 43 0
      app/src/main/java/com/sunwin/visitorapp/utils/ImageInf.java
  59. 7 1
      app/src/main/java/com/sunwin/visitorapp/utils/LogUtil.java
  60. 41 0
      app/src/main/java/com/sunwin/visitorapp/utils/NV21ToBitmap.java
  61. 112 0
      app/src/main/java/com/sunwin/visitorapp/utils/SystemTTS.java
  62. 6 0
      app/src/main/java/com/sunwin/visitorapp/utils/ToastUtils.java
  63. 46 0
      app/src/main/java/com/sunwin/visitorapp/utils/UniversalImageLoaderConfiguration.java
  64. 255 0
      app/src/main/java/com/sunwin/visitorapp/view/FaceDetecterView.java
  65. 29 0
      app/src/main/java/com/zkteco/android/IDReader/IDPhotoHelper.java
  66. 15 0
      app/src/main/java/com/zkteco/android/IDReader/WLTService.java
  67. 25 12
      app/src/main/res/layout/activity_id_card_reg.xml
  68. 0 3
      app/src/main/res/layout/activity_visitor_reg.xml
  69. 27 0
      app/src/main/res/layout/adapter_card.xml
  70. 27 0
      app/src/main/res/layout/view_face_detecter.xml
  71. BIN
      app/src/main/res/mipmap-xhdpi/id_card.png
  72. 0 1
      app/src/main/res/values/strings.xml

+ 25 - 0
app/build.gradle

@@ -26,6 +26,11 @@ android {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
     }
+    sourceSets {
+        main {
+            jniLibs.srcDirs = ['libs']
+        }
+    }
 }
 
 dependencies {
@@ -33,7 +38,27 @@ dependencies {
     implementation 'androidx.appcompat:appcompat:1.3.0-alpha02'
     implementation 'com.google.android.material:material:1.2.1'
     implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
+    implementation files('libs\\zkandroididcardreader.jar')
+    implementation files('libs\\zkandroidcore.jar')
+    implementation files('libs\\universal-image-loader-1.9.3.jar')
     testImplementation 'junit:junit:4.+'
     androidTestImplementation 'androidx.test.ext:junit:1.1.2'
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
+
+    //旷视算法
+    implementation files('libs/FacePassAndroidSDK-McvSafe-release.aar')
+    implementation files('libs/mcvSafe.v1.2.2.aar')
+
+    implementation 'com.guo.android_extend:android-extend:1.0.1'
+    implementation('com.jakewharton:butterknife:8.5.1') {
+        exclude group: 'com.android.support', module: 'support-annotations'
+        exclude group: 'com.android.support', module: 'support-compat'
+    }
+
+    implementation 'org.greenrobot:eventbus:3.1.1'
+    //retrofit
+    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
+    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
+    implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
 }

BIN
app/libs/FacePassAndroidSDK-McvSafe-release.aar


BIN
app/libs/armeabi-v7a/libwlt2bmp.so


BIN
app/libs/armeabi-v7a/libzkwltdecode.so


BIN
app/libs/mcvSafe.v1.2.2.aar


BIN
app/libs/universal-image-loader-1.9.3.jar


BIN
app/libs/zkandroidcore.jar


BIN
app/libs/zkandroididcardreader.jar


+ 0 - 7
app/src/main/AndroidManifest.xml

@@ -8,12 +8,6 @@
 
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.CAMERA" />
-
-    <!-- 显示系统窗口权限 -->
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
-    <!-- 在 屏幕最顶部显示权限-->
-    <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
-
     <application
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
@@ -32,7 +26,6 @@
         <activity android:name=".LoginActivity" />
         <activity android:name=".activity.VisitorRegActivity" />
         <activity android:name=".activity.IdcardRegActivity" />
-        <activity android:name=".activity.HomeAc" />
     </application>
 
 </manifest>

+ 1 - 0
app/src/main/assets/CBG_Android_Face_Reco---36502-Formal-one-stage.cert

@@ -0,0 +1 @@
+"{""serial"":""m000693e3dbd9878a41f69b32d85c0ef47e09"",""key"":""f4588b47d4bbb0c38dc1e4f42783dd827ab6ba8a7244b4fd25750733b4b76bba4f951c6f3b132e0c6ded0a2c8aa852112ab2889501a78ec64430674bba1911a3cb4d3aec7ad2f9f358c0aa76bf54e7fec58e94b326d96f5563299f8c99d48663a26a2c44d9d937b9685555ca3c2fccf3c13d5248631822d59edfd8e97e619472c8e0cce5d70b3044f1d22f9cd7e579e9f78c74d2686ca1bc38f80edea386a227db0d8be1bd370d45c99a5045459c8a73e0c2aca95a94a504700c93a7eab229f0c6fe7e2f9184b1b0""}"

BIN
app/src/main/assets/attr.RC.gray.12M.arm.200229.bin


BIN
app/src/main/assets/attr.age_gender.surveillance.nnie.av200.0.1.0.190630.bin


BIN
app/src/main/assets/attr.pose_blur.align.av200.190630.bin


BIN
app/src/main/assets/attr.smile.mgf29.0.1.1.181229.bin


BIN
app/src/main/assets/detector.arm.E.bin


BIN
app/src/main/assets/detector_rect.arm.E.bin


BIN
app/src/main/assets/feat2.arm.H.v1.0_1core.bin


BIN
app/src/main/assets/fonts/NeogreyRegular.otf


BIN
app/src/main/assets/fonts/Univers Condensed.ttf


BIN
app/src/main/assets/fonts/Univers LT 57 Condensed.ttf


BIN
app/src/main/assets/fonts/hua.ttf


BIN
app/src/main/assets/fonts/kai.ttf


BIN
app/src/main/assets/fonts/spyagency3.ttf


BIN
app/src/main/assets/liveness.CPU.rgb.int8.E.bin


BIN
app/src/main/assets/liveness.CPU.rgbir.int8.E.bin


BIN
app/src/main/assets/liveness.GPU.AlgoPolicy.E.cache


BIN
app/src/main/assets/liveness.GPU.rgb.E.bin


BIN
app/src/main/assets/liveness.GPU.rgbir.E.bin


BIN
app/src/main/assets/occlusion.all_attr_configurable.occ.190816.bin


BIN
app/src/main/assets/pf.lmk.arm.D.bin


+ 120 - 7
app/src/main/java/com/sunwin/visitorapp/SplashActivity.java

@@ -1,27 +1,43 @@
 package com.sunwin.visitorapp;
 
 import android.Manifest;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.core.app.ActivityCompat;
 
+import com.srp.AuthApi.AuthApi;
+import com.srp.AuthApi.AuthApplyResponse;
+import com.srp.AuthApi.ErrorCodeConfig;
+import com.sunwin.visitorapp.face.FRAbsLoopFactory;
 import com.sunwin.visitorapp.utils.AppManager;
-import com.sunwin.visitorapp.utils.Constants;
+import com.sunwin.visitorapp.utils.Constant;
+import com.sunwin.visitorapp.utils.LogUtil;
 import com.sunwin.visitorapp.utils.SharePrefenceUtils;
 import com.sunwin.visitorapp.utils.ToastUtils;
 
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.List;
 
 public class SplashActivity extends AppCompatActivity {
 
@@ -29,6 +45,7 @@ public class SplashActivity extends AppCompatActivity {
     private static final String TAG = "SplashActivity";
     private ArrayList<String> permissions;
     MyHandler myHandler = new MyHandler(this);
+    private AuthApi authApi;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -36,6 +53,7 @@ public class SplashActivity extends AppCompatActivity {
         setContentView(R.layout.activity_splash);
         AppManager.getAppManager().addActivity(this);
         permissions = new ArrayList<>();
+        authApi = new AuthApi();
         initData();
         myHandler.sendEmptyMessageDelayed(0, 500);
     }
@@ -47,10 +65,71 @@ public class SplashActivity extends AppCompatActivity {
         if (myHandler != null) {
             myHandler.removeMessages(0);
         }
+        unregisterReceiver(mUsbReceiver);
     }
 
     private void initData() {
+        authPermission();
         initPermission();
+        requestDevicePermission();
+    }
+
+    private void authPermission() {
+
+//        String cert = readExternal(CERT_PATH).trim();
+        String cert = readFile(this);
+        if (TextUtils.isEmpty(cert)) {
+//            Toast.makeText(this, "cert is null", Toast.LENGTH_SHORT).show();
+            return;
+        }
+
+        authApi.authDevice(this.getApplicationContext(), cert, "", new AuthApi.AuthDeviceCallBack() {
+            @Override
+            public void GetAuthDeviceResult(final AuthApplyResponse result) {
+                if (result.errorCode == ErrorCodeConfig.AUTH_SUCCESS) {
+
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            ToastUtils.showToast("Apply update: OK");
+                            SharePrefenceUtils.putBoolean(Constant.ISharePrefence.ISINIT, true);
+                            new FRAbsLoopFactory().createFRAblLoop(SplashActivity.this, Constant.NumerValue.FRABS_TYPE);
+                        }
+                    });
+                } else {
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            ToastUtils.showToast("Apply update: error. error code is: " + result.errorCode + " , error message: " + result.errorMessage);
+                        }
+                    });
+                }
+            }
+        });
+
+    }
+
+    public String readFile(Context context) {
+        String content = "";
+        try {
+            InputStream instream = context.getAssets().open("CBG_Android_Face_Reco---36502-Formal-one-stage.cert");
+            if (instream != null) {
+                InputStreamReader inputreader = new InputStreamReader(instream);
+                String line;
+                for (BufferedReader buffreader = new BufferedReader(inputreader);
+                     (line = buffreader.readLine()) != null;
+                     content = line) {
+                }
+                instream.close();
+            } else {
+                ToastUtils.showToast("未找到文件");
+            }
+        } catch (FileNotFoundException var8) {
+            LogUtil.d(TAG, "The File doesn't not exist.");
+        } catch (IOException var9) {
+            LogUtil.d(TAG, var9.getMessage());
+        }
+        return content;
     }
 
     private void initPermission() {
@@ -107,9 +186,6 @@ public class SplashActivity extends AppCompatActivity {
     }
 
 
-
-
-
     @Override
     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
         super.onRequestPermissionsResult(requestCode, permissions, grantResults);
@@ -132,7 +208,7 @@ public class SplashActivity extends AppCompatActivity {
      * 进入应用程序
      */
     private void openApplication() {
-        boolean isLogin = SharePrefenceUtils.getBoolean(Constants.ISharePrefence.LOGINTAG, false);
+        boolean isLogin = SharePrefenceUtils.getBoolean(Constant.ISharePrefence.LOGINTAG, false);
         if (isLogin) {
             gotoMain();
         } else {
@@ -151,4 +227,41 @@ public class SplashActivity extends AppCompatActivity {
         startActivity(intent);
         finish();
     }
+
+    private final String ACTION_USB_PERMISSION = "com.sunwin.visitorapp.USB_PERMISSION";
+
+    private void requestDevicePermission() {
+        UsbManager musbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_USB_PERMISSION);
+        filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
+        registerReceiver(mUsbReceiver, filter);
+
+        for (UsbDevice device : musbManager.getDeviceList().values()) {
+            if (device.getVendorId() == Constant.NumerValue.VID && device.getProductId() == Constant.NumerValue.PID) {
+                Intent intent = new Intent(ACTION_USB_PERMISSION);
+                PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
+                musbManager.requestPermission(device, pendingIntent);
+            }
+        }
+    }
+
+    private BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            if (ACTION_USB_PERMISSION.equals(action)) {
+                synchronized (this) {
+                    UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                    if (!intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
+                        ToastUtils.showToast(getString(R.string.usb_unauthorized));
+                    } else {
+                        LogUtil.e(TAG, "-----");
+                    }
+                }
+            }
+        }
+    };
+
 }

+ 116 - 8
app/src/main/java/com/sunwin/visitorapp/activity/IdcardRegActivity.java

@@ -1,17 +1,29 @@
 package com.sunwin.visitorapp.activity;
 
-import android.content.Intent;
+import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.view.View;
+import android.widget.AdapterView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.sunwin.visitorapp.BaseActivity;
+import com.sunwin.visitorapp.BaseApplication;
 import com.sunwin.visitorapp.R;
+import com.sunwin.visitorapp.adapter.CardAdapter;
+import com.sunwin.visitorapp.face.IDCardReaderUtil;
+import com.sunwin.visitorapp.face.IdCardReadCallback;
+import com.sunwin.visitorapp.model.OpenDoorByFaceNetResultInfo;
+import com.sunwin.visitorapp.utils.LogUtil;
+import com.sunwin.visitorapp.utils.ToastUtils;
+import com.sunwin.visitorapp.view.FaceDetecterView;
 import com.sunwin.visitorapp.view.MyGridView;
 
-public class IdcardRegActivity extends BaseActivity implements View.OnClickListener {
+import java.util.ArrayList;
+import java.util.List;
+
+public class IdcardRegActivity extends BaseActivity implements View.OnClickListener, AdapterView.OnItemClickListener, IdCardReadCallback, FaceDetecterView.Listener {
     private ImageView mHeadIvBack;
     private TextView mTvHeadTitle;
     private TextView mTvRight;
@@ -19,29 +31,125 @@ public class IdcardRegActivity extends BaseActivity implements View.OnClickListe
     private LinearLayout mLlCardReg;
     private MyGridView mGridview;
 
+    private FaceDetecterView mFaceDetecterView;
+
+    private List<String> itemList;
+    private List<Integer> iconList;
+    private CardAdapter mAdapter;
+
+    private IDCardReaderUtil idCardReaderUtil;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_id_card_reg);
+        itemList = new ArrayList<>();
+        iconList = new ArrayList<>();
+        idCardReaderUtil = new IDCardReaderUtil(this, this);
+        idCardReaderUtil.startRead();
         initView();
+        initData();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mFaceDetecterView.onResume();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        idCardReaderUtil.stopRead();
+        mFaceDetecterView.onDestroy();
+    }
+
+    private void initData() {
+        itemList.add("访客记录");
+        itemList.add("访客签到");
+        itemList.add("访客登记");
+        itemList.add("访客签离");
+        itemList.add("快速核验");
+        iconList.add(R.mipmap.ic_launcher);
+        iconList.add(R.mipmap.ic_launcher);
+        iconList.add(R.mipmap.ic_launcher);
+        iconList.add(R.mipmap.ic_launcher);
+        iconList.add(R.mipmap.ic_launcher);
+        mAdapter.notifyDataSetChanged();
     }
 
     private void initView() {
-        mHeadIvBack = (ImageView) findViewById(R.id.head_iv_back);
+        findViewById(R.id.head_iv_back).setOnClickListener(this);
         mTvHeadTitle = (TextView) findViewById(R.id.tv_head_title);
         mTvRight = (TextView) findViewById(R.id.tv_right);
         mTvDate = (TextView) findViewById(R.id.tv_date);
-       findViewById(R.id.ll_id_card_reg).setOnClickListener(this);
-        mGridview = (MyGridView) findViewById(R.id.gridview);
-    }
-//    startActivity(new Intent(this, VisitorRegActivity.class)/
+        mLlCardReg = findViewById(R.id.ll_id_card_reg);
+        mFaceDetecterView = findViewById(R.id.faceDetecterView);
+        mGridview = findViewById(R.id.gridview);
+
+
+        mAdapter = new CardAdapter(itemList, iconList);
+        mGridview.setAdapter(mAdapter);
+        mGridview.setOnItemClickListener(this);
 
+        mFaceDetecterView.config(this, this);
+
+        mTvHeadTitle.setText("访客登记");
+
+    }
 
     @Override
     public void onClick(View view) {
         switch (view.getId()) {
-            case R.id.ll_id_card_reg:
+            case R.id.head_iv_back:
+                finish();
                 break;
         }
     }
+
+    @Override
+    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
+
+    }
+
+    @Override
+    public void onIdNumRead(String idNum) {
+        LogUtil.e(TAG, "idNum = " + idNum);
+        //读取身份证后显示人脸扫描框
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mLlCardReg.setVisibility(View.GONE);
+                ToastUtils.showToast(getString(R.string.have_readed_id_card));
+                mFaceDetecterView.setVisibility(View.VISIBLE);
+            }
+        });
+    }
+
+    /**
+     * ------------------FaceDetecterView.Listener 开始------------------------------
+     */
+
+    @Override
+    public void detectedFace() {
+
+    }
+
+    @Override
+    public void detectedUser(OpenDoorByFaceNetResultInfo info) {
+        BaseApplication.getSystemTTS().playText("识别成功");
+        // TODO: 2022/9/14  跳转到访客信息完善页面
+        LogUtil.e(TAG, "跳转到访客信息完善页面");
+    }
+
+    @Override
+    public void detecterFail(String st) {
+        BaseApplication.getSystemTTS().playText("识别失败");
+    }
+
+    @Override
+    public void takePic(Bitmap bitmap) {
+
+    }
+    /** ------------------FaceDetecterView.Listener 结束------------------------------  */
 }

+ 2 - 0
app/src/main/java/com/sunwin/visitorapp/activity/VisitorRegActivity.java

@@ -1,5 +1,6 @@
 package com.sunwin.visitorapp.activity;
 
+import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
 import android.widget.ImageView;
@@ -37,6 +38,7 @@ public class VisitorRegActivity extends BaseActivity implements View.OnClickList
     public void onClick(View view) {
         switch (view.getId()) {
             case R.id.ll_card_reg:
+                startActivity(new Intent(this, IdcardRegActivity.class));
                 break;
             case R.id.ll_no_card_reg:
                 break;

+ 69 - 0
app/src/main/java/com/sunwin/visitorapp/adapter/CardAdapter.java

@@ -0,0 +1,69 @@
+package com.sunwin.visitorapp.adapter;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.sunwin.visitorapp.BaseApplication;
+import com.sunwin.visitorapp.R;
+
+import java.util.List;
+
+public class CardAdapter extends BaseAdapter {
+    LayoutInflater inflater;
+    private List<String> itemList;
+    private List<Integer> iconRes;
+    public CardAdapter(List<String> itemList,List<Integer> iconRes) {
+        this.itemList = itemList;
+        this.iconRes = iconRes;
+        inflater = LayoutInflater.from(BaseApplication.getInstance());
+    }
+
+
+    @Override
+    public int getCount() {
+        return itemList.size();
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return itemList.get(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ViewHolder holder = null;
+        if (convertView == null) {
+            convertView = inflater.inflate(R.layout.adapter_card, parent, false);
+            holder = new ViewHolder(convertView);
+            convertView.setTag(holder);
+        } else {
+            holder = (ViewHolder) convertView.getTag();
+        }
+        holder.item_desc.setText(itemList.get(position));
+        holder.item_icon.setImageResource(iconRes.get(position));
+
+        return convertView;
+    }
+
+
+    public static class ViewHolder {
+        public ImageView item_icon;
+        public TextView item_desc;
+
+        public ViewHolder(View rootView) {
+            this.item_icon = rootView.findViewById(R.id.item_icon);
+            this.item_desc = rootView.findViewById(R.id.item_desc);
+        }
+
+    }
+}

+ 222 - 0
app/src/main/java/com/sunwin/visitorapp/camera/CameraHolder.java

@@ -0,0 +1,222 @@
+package com.sunwin.visitorapp.camera;
+
+import android.graphics.ImageFormat;
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.SurfaceHolder;
+import android.view.WindowManager;
+
+import com.sunwin.visitorapp.utils.LogUtil;
+
+import java.util.List;
+
+/**
+ * Camera持有者,根据WindowManager自动选择合适的 宽、高、数据格式
+ * Created by shitianci on 2018/7/18.
+ */
+
+public class CameraHolder implements CameraSurfaceView.OnCameraListener {
+    private static final String TAG = CameraHolder.class.getSimpleName();
+    int mCameraID = Camera.CameraInfo.CAMERA_FACING_BACK;
+    int mCameraRotate = 0;
+    boolean mCameraMirror = false;
+    private int mWidth, mHeight, mFormat;
+    public Camera mCamera;
+    private Camera.Parameters parameters;//相机参数
+    private CameraSurfaceView mSurfaceView;
+    private Camera.AutoFocusCallback myAutoFocusCallback = null;
+    private boolean mPreviewRunning = false;
+    public SurfaceHolder mSurfaceHolder;
+
+    /**
+     * 配置摄像头属性,必须在
+     *
+     * @param cameraID     Camera.CameraInfo.CAMERA_FACING_BACK or Camera.CameraInfo.CAMERA_FACING_FRONT
+     * @param cameraRotate
+     * @param cameraMirror
+     */
+    public void configCamera(int cameraID, int cameraRotate, boolean cameraMirror) {
+        this.mCameraID = cameraID;
+        this.mCameraRotate = cameraRotate;
+        this.mCameraMirror = cameraMirror;
+    }
+
+    /**
+     * 配置显示组件
+     */
+    public void configView(WindowManager windowManager, CameraSurfaceView cameraSurfaceView) {
+        mSurfaceHolder = cameraSurfaceView.getHolder();
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        Display display = windowManager.getDefaultDisplay();
+        display.getMetrics(displayMetrics);
+        Point size = new Point();
+        display.getSize(size);
+        configView(size, cameraSurfaceView);
+        myAutoFocusCallback = new Camera.AutoFocusCallback() {
+            public void onAutoFocus(boolean success, Camera camera) {
+                if (success)//success表示对焦成功
+                {
+                    Log.i(TAG, "onAutoFocus succeed...");
+                    camera.cancelAutoFocus();//只有加上了这一句,才会自动对焦。
+//                    initCamera(mSurfaceHolder);
+//                    doAutoFocus();
+                } else {
+                    Log.i(TAG, "onAutoFocus failed...");
+                }
+            }
+        };
+
+    }
+
+    /**
+     * 配置显示组件
+     */
+    public void configView(Point size, CameraSurfaceView cameraSurfaceView) {
+        this.mSurfaceView = cameraSurfaceView;
+        mWidth = size.x;
+        mHeight = size.y;
+        mFormat = ImageFormat.NV21;
+
+        cameraSurfaceView.setOnCameraListener(this);
+
+    }
+
+    /**
+     * 通过对比得到与宽高比最接近的预览尺寸(如果有相同尺寸,优先选择)
+     *
+     * @param isPortrait    是否竖屏
+     * @param surfaceWidth  需要被进行对比的原宽
+     * @param surfaceHeight 需要被进行对比的原高
+     * @param preSizeList   需要对比的预览尺寸列表
+     * @return 得到与原宽高比例最接近的尺寸
+     */
+    public static Camera.Size getCloselyPreSize(boolean isPortrait, int surfaceWidth, int surfaceHeight, List<Camera.Size> preSizeList) {
+        int reqTmpWidth;
+        int reqTmpHeight;
+        // 当屏幕为垂直的时候需要把宽高值进行调换,保证宽大于高
+        if (isPortrait) {
+            reqTmpWidth = surfaceHeight;
+            reqTmpHeight = surfaceWidth;
+        } else {
+            reqTmpWidth = surfaceWidth;
+            reqTmpHeight = surfaceHeight;
+        }
+        //先查找preview中是否存在与surfaceview相同宽高的尺寸
+        for (Camera.Size size : preSizeList) {
+            if (((size.width == reqTmpWidth) && (size.height == reqTmpHeight)) || ((size.width == reqTmpHeight) && (size.height == reqTmpWidth))) {
+                return size;
+            }
+        }
+
+        Camera.Size retSize = null;
+        if (true) {
+            // 得到与传入的宽高比最接近的size : 这种方式有问题,可能获取到像素很低的画面,应该计算宽高最接近的size才对
+            float reqRatio = ((float) reqTmpWidth) / reqTmpHeight;
+            float curRatio, deltaRatio;
+            float deltaRatioMin = Float.MAX_VALUE;
+            for (Camera.Size size : preSizeList) {
+                curRatio = ((float) size.width) / size.height;
+                deltaRatio = Math.abs(reqRatio - curRatio);
+                if (deltaRatio < deltaRatioMin) {
+                    deltaRatioMin = deltaRatio;
+                    retSize = size;
+                }
+            }
+        } else {
+            int result = Integer.MAX_VALUE;
+            for (Camera.Size size : preSizeList) {
+                int result2 = Math.abs(size.width - surfaceWidth) + Math.abs(size.height - surfaceHeight);
+                if (result2 < result) {
+                    result = result2;
+                    retSize = size;
+                }
+            }
+        }
+
+        return retSize;
+    }
+
+
+    @Override
+    public Camera setupCamera() {
+        mCamera = Camera.open(mCameraID);
+
+        if (mCamera != null)
+            LogUtil.e(TAG, "carmera=" + mCameraID + "");
+        try {
+            int screenWidth = mWidth;
+            int screenHeight = mHeight;
+            Camera.Parameters parameters = mCamera.getParameters();
+            LogUtil.d(TAG, "screenSIZE:" + screenWidth + "x" + screenHeight);
+            Camera.Size preSize = getCloselyPreSize(true, screenWidth, screenHeight, parameters.getSupportedPreviewSizes());
+            LogUtil.d(TAG, "PreSIZE:" + preSize.width + "x" + preSize.height);
+
+            parameters.setPreviewSize(preSize.width, preSize.height);
+//            parameters.setPreviewSize(screenHeight, screenWidth);
+            parameters.setPreviewFormat(mFormat);
+
+            for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
+                LogUtil.d(TAG, "SIZE:" + size.width + "x" + size.height);
+            }
+            for (Integer format : parameters.getSupportedPreviewFormats()) {
+                LogUtil.d(TAG, "FORMAT:" + format);
+            }
+
+            List<int[]> fps = parameters.getSupportedPreviewFpsRange();
+            for (int[] count : fps) {
+                Log.d(TAG, "T:");
+                for (int data : count) {
+                    Log.d(TAG, "V=" + data);
+                }
+            }
+
+            mCamera.setParameters(parameters);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        if (mCamera != null) {
+            mWidth = mCamera.getParameters().getPreviewSize().width;
+            mHeight = mCamera.getParameters().getPreviewSize().height;
+        }
+        LogUtil.e(TAG, "------------setupCamera---------mCamera = " + mCamera);
+        return mCamera;
+    }
+
+    @Override
+    public void setupChanged(int format, int width, int height) {
+    }
+
+    @Override
+    public boolean startPreviewLater() {
+        return false;
+    }
+
+    @Override
+    public Object onPreview(byte[] data, int width, int height, int format, long timestamp) {
+        return null;
+    }
+
+    @Override
+    public void stopCamera() {
+        LogUtil.e(TAG, "------------stopCamera---------");
+        mSurfaceView.surfaceDestroyed(mSurfaceHolder);
+    }
+
+    @Override
+    public void reStartCamera() {
+        LogUtil.e(TAG, "------------reStartCamera---------");
+        mSurfaceView.reStartCamera();
+    }
+
+
+    @Override
+    public void onViewDestory() {
+        LogUtil.e(TAG, "------------onViewDestory---------");
+        mSurfaceView.surfaceDestroyed(mSurfaceHolder);
+    }
+
+
+}

+ 218 - 0
app/src/main/java/com/sunwin/visitorapp/camera/CameraSurfaceView.java

@@ -0,0 +1,218 @@
+package com.sunwin.visitorapp.camera;
+
+/**
+ * Created by shitianci on 2017/12/27.
+ */
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.hardware.Camera.PreviewCallback;
+import android.hardware.Camera.Size;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import com.guo.android_extend.tools.FrameHelper;
+import com.sunwin.visitorapp.utils.Constant;
+import com.sunwin.visitorapp.utils.LogUtil;
+
+/**
+ * create by gqjjqg,.
+ * easy to use camera.
+ */
+public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, PreviewCallback {
+    private final String TAG = this.getClass().getSimpleName();
+
+    private Camera mCamera;
+    private int mWidth, mHeight, mFormat;
+    private OnCameraListener mOnCameraListener;
+    private FrameHelper mFrameHelper;
+
+
+    private int roateValue;
+    private boolean isInitCamera = false;
+
+    public interface OnCameraListener {
+        /**
+         * setup camera.
+         *
+         * @return the camera
+         */
+        public Camera setupCamera();
+
+        /**
+         * reset on surfaceChanged.
+         *
+         * @param format image format.
+         * @param width  width
+         * @param height height.
+         */
+        public void setupChanged(int format, int width, int height);
+
+        /**
+         * start preview immediately, after surfaceCreated
+         *
+         * @return true or false.
+         */
+        public boolean startPreviewLater();
+
+        /**
+         * on ui thread.
+         *
+         * @param data      image data
+         * @param width     width
+         * @param height    height
+         * @param format    format
+         * @param timestamp time stamp
+         * @return image params.
+         */
+        public Object onPreview(byte[] data, int width, int height, int format, long timestamp);
+
+        public void stopCamera();
+
+        public void reStartCamera();
+
+        public void onViewDestory();
+    }
+
+    public CameraSurfaceView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        // TODO Auto-generated constructor stub
+        onCreate();
+    }
+
+    public CameraSurfaceView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        // TODO Auto-generated constructor stub
+        onCreate();
+
+    }
+
+    public CameraSurfaceView(Context context) {
+        super(context);
+        // TODO Auto-generated constructor stub
+        onCreate();
+    }
+
+    private void onCreate() {
+        SurfaceHolder arg0 = getHolder();
+        arg0.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+        arg0.addCallback(this);
+
+        mFrameHelper = new FrameHelper();
+
+
+    }
+
+    private boolean openCamera() {
+        try {
+
+            if (mCamera != null && isInitCamera) {
+                mCamera.reconnect();
+            } else {
+                if (mOnCameraListener != null) {
+                    mCamera = mOnCameraListener.setupCamera();
+                    isInitCamera = true;
+                }
+            }
+
+            if (mCamera != null) {
+                Camera.Parameters parameters = mCamera.getParameters();
+                parameters.setPreviewSize(640, 480);
+                parameters.setPictureSize(640, 480);
+
+                mCamera.setParameters(parameters);
+                mCamera.setPreviewDisplay(getHolder());
+
+                Size imageSize = mCamera.getParameters().getPreviewSize();
+                mWidth = imageSize.width;
+                mHeight = imageSize.height;
+                mFormat = mCamera.getParameters().getPreviewFormat();
+                int lineBytes = imageSize.width * ImageFormat.getBitsPerPixel(mFormat) / 8;
+                mCamera.addCallbackBuffer(new byte[lineBytes * mHeight]);
+
+
+                mCamera.setPreviewCallbackWithBuffer(this);
+
+                return true;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        // TODO Auto-generated method stub
+        if (mOnCameraListener != null) {
+            mOnCameraListener.setupChanged(format, width, height);
+        }
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        LogUtil.e(TAG,"---surfaceCreated()------mCamera = "+mCamera+",mOnCameraListener ="+mOnCameraListener);
+        // TODO Auto-generated method stub
+        if (openCamera()) {
+            Log.d(TAG, "preview size = "
+                    + mCamera.getParameters().getPreviewSize().width + ","
+                    + mCamera.getParameters().getPreviewSize().height);
+            if (mOnCameraListener != null) {
+                if (!mOnCameraListener.startPreviewLater()) {
+                    mCamera.setDisplayOrientation(Constant.rotate);
+                    mCamera.startPreview();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        LogUtil.e(TAG,"-------surfaceDestroyed()----------------");
+        // TODO Auto-generated method stub
+        if (mCamera != null) {
+            LogUtil.e(TAG,"-------mCamera release----------------");
+            mCamera.setPreviewCallbackWithBuffer(null);
+            mCamera.stopPreview();
+            mCamera.release();
+            mCamera = null;
+        }
+
+    }
+
+    @Override
+    public void onPreviewFrame(byte[] data, Camera camera) {
+        // TODO Auto-generated method stub
+//        Log.e("onPreviewFrame","onPreviewFrame");
+        long timestamp = System.nanoTime();
+//        mFrameHelper.printFPS();
+
+        if (mOnCameraListener != null) {
+            mOnCameraListener.onPreview(data.clone(), mWidth, mHeight, mFormat, timestamp);
+        }
+
+        if (mCamera != null) {
+            mCamera.addCallbackBuffer(data);
+        }
+    }
+
+
+    public void setOnCameraListener(OnCameraListener l) {
+        LogUtil.e(TAG,"--------setOnCameraListener----------"+l);
+        mOnCameraListener = l;
+    }
+
+
+    public void debug_print_fps(boolean preview, boolean render) {
+        mFrameHelper.enable(preview);
+    }
+
+    public void reStartCamera() {
+        openCamera();
+        mCamera.setDisplayOrientation(Constant.rotate);
+        mCamera.startPreview();
+    }
+}

+ 22 - 0
app/src/main/java/com/sunwin/visitorapp/face/CameraPreviewData.java

@@ -0,0 +1,22 @@
+package com.sunwin.visitorapp.face;
+
+public class CameraPreviewData {
+    public byte[] nv21Data;
+
+    public int width;
+
+    public int height;
+
+    public int rotation;
+
+    public boolean mirror;
+
+    public CameraPreviewData(byte[] nv21Data, int width, int height, int rotation, boolean mirror) {
+        super();
+        this.nv21Data = nv21Data;
+        this.width = width;
+        this.height = height;
+        this.rotation = rotation;
+        this.mirror = mirror;
+    }
+}

+ 22 - 0
app/src/main/java/com/sunwin/visitorapp/face/DetectResult.java

@@ -0,0 +1,22 @@
+package com.sunwin.visitorapp.face;
+
+public class DetectResult {
+    byte[] detectResult;
+    CameraPreviewData cameraPreviewData;
+
+    public byte[] getDetectResult() {
+        return detectResult;
+    }
+
+    public void setDetectResult(byte[] detectResult) {
+        this.detectResult = detectResult;
+    }
+
+    public CameraPreviewData getCameraPreviewData() {
+        return cameraPreviewData;
+    }
+
+    public void setCameraPreviewData(CameraPreviewData cameraPreviewData) {
+        this.cameraPreviewData = cameraPreviewData;
+    }
+}

+ 368 - 0
app/src/main/java/com/sunwin/visitorapp/face/FRAbsLoop.java

@@ -0,0 +1,368 @@
+package com.sunwin.visitorapp.face;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.graphics.YuvImage;
+import android.os.Environment;
+import android.util.Log;
+
+import com.guo.android_extend.java.AbsLoop;
+import com.guo.android_extend.java.ExtByteArrayOutputStream;
+import com.sunwin.visitorapp.model.UserModel;
+import com.sunwin.visitorapp.model.bus.DescEvent;
+import com.sunwin.visitorapp.utils.BitmapUtils;
+import com.sunwin.visitorapp.utils.Constant;
+import com.sunwin.visitorapp.utils.ConvertUtil;
+import com.sunwin.visitorapp.utils.SharePrefenceUtils;
+import com.zkteco.android.biometric.module.idcard.meta.IDCardInfo;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 集成引擎步骤:
+ * 1. 实现抽象函数 initialEngine、onPreview、searchFaceLocally、uninitialEngine。
+ * 2. 构造全局单例
+ * <p>
+ * Created by shitianci on 2018/7/6.
+ */
+
+public abstract class FRAbsLoop {
+
+    static final String TAG = FRAbsLoop.class.getSimpleName();
+
+    protected volatile IFaceInfo mCurFaceInfo; //当前正在处理的人脸信息
+    public IDCardInfo idCardInfo;//刷身份证开门时的身份证信息
+    public UserModel userModel;//刷卡卡门时用户信息
+    public static int DETECTER_BACK_TYPE = 0; //后台同步提取的数据
+    public static int DETECTER_FRONT_TYPE = 1;//前端提取的数据
+
+    //初始化引擎
+    public abstract void initialEngine(Context context);
+
+    //处理静态图片 ,返回人脸矩阵
+    public abstract List<Rect> faceDetection(Bitmap bitmap, int type);//加上类别,区分不同的特征提取
+
+    public Activity currentActivity;
+
+    /**
+     * 检测文件是否有人脸,有则返回bitmap, 否则返回null;
+     *
+     * @param file 文件命名的规则如下: 姓名_ID.后缀名  ID必须具有唯一性。
+     * @return
+     */
+    public abstract boolean detectFace(File file, int type);
+
+    //处理动态图片
+    public Rect[] onPreview(byte[] data, int width, int height, int format, long timestamp) {
+        setWH(width, height);
+        return null;
+    }
+
+//    //识别人脸
+//    public OpenDoorByFaceNetResultInfo searchFaceLocally(byte[] imageNV21, Rect rect, String face_image) {
+//        OpenDoorByFaceNetResultInfo openDoorByFaceNetResultInfo = new OpenDoorByFaceNetResultInfo();
+//        openDoorByFaceNetResultInfo.setCode(-1);
+//        if (SpUtils.getBoolean("isIdNumberOpenDoor", false)) {
+//            if (idCardInfo == null) {
+//                openDoorByFaceNetResultInfo.setMessage(DescEvent.DESC_FR_TIP_2);
+//            } else {
+//                openDoorByFaceNetResultInfo.setMessage(DescEvent.DESC_FR_TIP);
+//            }
+//
+//        } else if (SpUtils.getBoolean("iscardCheckFace", false)) {
+//            openDoorByFaceNetResultInfo.setMessage(DescEvent.DESC_NEED_FLUSH_CARD);
+//        } else {
+//            openDoorByFaceNetResultInfo.setMessage(DescEvent.DESC_FR_TIP);
+//        }
+//
+//
+//        return openDoorByFaceNetResultInfo;
+//    }
+
+    //保存人脸
+    public abstract void saveFace(UserModel userModel);
+
+    public void editSaveFace(UserModel userModel) {
+    }
+
+
+    //销毁引擎
+    public abstract void uninitialEngine();
+
+    public volatile boolean isCanVerify; //是否能进行识别,弹窗的时候就暂停抓取图片识别
+    public volatile boolean isVerifying; //是否正在识别
+    public volatile boolean isTimeWrong; //是否正在识别
+
+    int mWidth;
+    int mHeight;
+    private long mS_BeforeSearchFace; //统计 检测到人脸,到执行searchFace 的时间
+
+
+
+    public void setup() {
+    }
+
+
+    public void loop() {
+        if (!isCanVerify) {
+            Log.w(TAG, "弹窗时,不需要进行识别");
+            return;
+        }
+        if (isVerifying) {
+//            Log.w(TAG, "正在识别中");
+            return;
+        }
+        if (mCurFaceInfo == null) {
+//            Log.w(TAG,"没有检测到人脸");
+            return;
+        }
+
+
+        synchronized (FRAbsLoop.class) {
+            isVerifying = true;
+            searchFace();
+        }
+    }
+
+
+    public void searchFace() {
+//        Log.e(TAG, "进入人脸检索");
+        if (mS_BeforeSearchFace == 0L) {
+
+        } else {
+            mS_BeforeSearchFace = System.currentTimeMillis() - mS_BeforeSearchFace;
+
+            mS_BeforeSearchFace = 0;
+        }
+        EventBus.getDefault().post(new DescEvent().setDesc(DescEvent.DESC_FR_ING));
+
+        //crop
+//        saveImageNV21(mCurFaceInfo.getImage(), mWidth, mHeight, mCurFaceInfo.getImageRect(), 100, TimeUtil.longToString(System.currentTimeMillis(), "yyyyMMddHHmmss") + "识别1" + ".jpg");
+        byte[] imageNV21 = mCurFaceInfo.getImage();
+        if (imageNV21 == null) {
+            Log.e(TAG, "走到这里,意味着数据冲突了");
+//            EventBus.getDefault().post(new DescEvent().setDesc("数据遗失"));
+            isVerifying = false;
+            try {
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+//            EventBus.getDefault().post(new DescEvent().setDesc(DescEvent.DESC_FR_TIP));
+            mCurFaceInfo = null;
+            return;
+        }
+
+        //裁剪头像数据
+        ExtByteArrayOutputStream ops = new ExtByteArrayOutputStream();
+        final Rect rect = mCurFaceInfo.getImageRect();
+//        适当放大框的比例
+//        int left = (int) (rect.left * 0.9);
+//        int top = (int) (rect.top * 0.9);
+//        int right = (int) (rect.right * 1.1);
+//        int bottom = (int) (rect.bottom * 1.1);
+//        rect.set(left, top, right, bottom);
+//        rect.set(0,0, mWidth, mHeight); //全屏幕截取
+        final YuvImage yuv = new YuvImage(imageNV21, ImageFormat.NV21, mWidth, mHeight, null);
+        try {
+            yuv.compressToJpeg(rect, 100, ops);
+        } catch (Exception e) {
+            e.printStackTrace();
+            mCurFaceInfo = null;
+            isVerifying = false;
+            return;
+        }
+
+//        saveImageNV21(imageNV21, mWidth, mHeight, rect, 80, TimeUtil.longToString(System.currentTimeMillis(), "yyyyMMddHHmmss") + "识别2" + ".jpg");
+
+        Bitmap bmp = BitmapFactory.decodeByteArray(ops.getByteArray(), 0, ops.getByteArray().length);
+        if (bmp == null) {
+            mCurFaceInfo = null;
+            isVerifying = false;
+            return;
+        }
+        try {
+            ops.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+            return;
+        }
+
+        Bitmap bitmap = null;
+        int rotate = 0;
+        if (Constant.isSufaRotate) {
+            rotate = 180;
+        } else {
+            rotate = 0;
+        }
+        if (Constant.isFrontCamera) {
+            //竖屏状态下,bmp会在服务端显示左转90度
+            bitmap = BitmapUtils.rotateBitmap1(bmp, Constant.rotate + rotate);
+        } else {
+            bitmap = BitmapUtils.rotateBitmap1(bmp, Constant.rotate + rotate);
+        }
+
+
+        bitmap = BitmapUtils.scaleBitmap(bitmap, 200, 200);
+
+//                bitmap = BitmapUtils.toGrayscale(BitmapUtils.scaleBitmap(bitmap, 96, 112));
+//
+//        final OpenDoorByFaceNetResultInfo openDoorByFaceNetResultInfo;
+//        final String face_image = ConvertUtil.bitmapToBase64(bitmap);
+//
+//        //本地识别
+//        openDoorByFaceNetResultInfo = searchFaceLocally(imageNV21, rect, face_image);
+//
+//
+////        Log.d(TAG, "OpenDoorByFaceNetResultInfo:" + openDoorByFaceNetResultInfo.toString());
+//        if (openDoorByFaceNetResultInfo.getCode() == 0) {
+//            EventBus.getDefault().post(new DescEvent().setDesc(DescEvent.DESC_FR_SUCC).setOpenDoorByFaceNetResultInfo(openDoorByFaceNetResultInfo));
+//            //成功了休眠
+//            try {
+//                sleep(Constant.NumerValue.TIME_CLOSE_DIALOG);
+//            } catch (InterruptedException e) {
+//                e.printStackTrace();
+//            }
+//            stopVerify();
+//            //TODO 放在识别线程,影响识别效率,需要移走
+////            uploadImageNV21(yuv, openDoorByFaceNetResultInfo.getPerson_name()
+////                    + "-" + openDoorByFaceNetResultInfo.getPerson_code()
+////                    + "-" + TimeUtil.longToString(System.currentTimeMillis(), "yyyyMMddHHmmss") + ".jpg");
+//        } else {
+//            //抛出失败原因
+//            EventBus.getDefault().post(new DescEvent().setDesc(openDoorByFaceNetResultInfo.getMessage()));
+//        }
+
+
+        mCurFaceInfo = null;
+        bmp.recycle();
+        bitmap.recycle();
+        startVerify();
+//        EventBus.getDefault().post(new DescEvent().setDesc(DescEvent.DESC_FR_TIP));
+
+    }
+
+    public static File saveImageNV21(byte[] imageNV21, int width, int height, Rect rect, int quality, String fileName) {
+//        Log.e(TAG, "imageNV21 is " + imageNV21 + "; fileName = " + fileName);
+        final YuvImage yuv = new YuvImage(imageNV21, ImageFormat.NV21, width, height, null);
+        return saveImageNV21(yuv, rect, quality, fileName);
+    }
+
+    //保存到本地 imageNV21
+    public static File saveImageNV21(YuvImage yuv, Rect rect, int quality, String fileName) {
+        File sdRoot = Environment.getExternalStorageDirectory();
+        String dir = "/DCIM/Camera/";
+        File mkDir = new File(sdRoot, dir);
+        if (!mkDir.exists()) {
+            mkDir.mkdirs();
+        }
+        File pictureFile = new File(sdRoot, dir + fileName);
+        try {
+            pictureFile.createNewFile();
+            FileOutputStream filecon = new FileOutputStream(pictureFile);
+            yuv.compressToJpeg(rect, quality, filecon);
+            try {
+                filecon.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return pictureFile;
+    }
+/*
+    //上传 imageNV21
+    private void uploadImageNV21(YuvImage yuv, String fileName) {
+        Rect rect = new Rect(0, 0, yuv.getWidth(), yuv.getHeight());
+        final File pictureFile = saveImageNV21(yuv, rect, 100, fileName);
+        List<String> paths = new ArrayList<>();
+        paths.add(pictureFile.getAbsolutePath());
+        RepositoryCollection.uploadFaceImg(paths);
+        pictureFile.delete();
+    }*/
+
+
+    public void over() {
+
+    }
+
+    public void start() {
+
+        startVerify();
+    }
+
+    public void shutdown() {
+        stopVerify();
+    }
+
+    /*开启人脸检测线程*/
+    public void startVerify() {
+        Log.d(TAG, "startVerify");
+        isCanVerify = true;
+        isVerifying = false;
+    }
+
+
+    private void stopVerify() {
+        Log.d(TAG, "stopVerify");
+        isCanVerify = false;
+    }
+
+    /**
+     * 设置画面宽高
+     *
+     * @param mWidth  宽度
+     * @param mHeight 高度
+     */
+    public void setWH(int mWidth, int mHeight) {
+//        Log.d(TAG, "setWH: " + mWidth + "*"+ mHeight);
+        this.mWidth = mWidth;
+        this.mHeight = mHeight;
+    }
+
+    /**
+     * 摄像线程:不断处理摄像数据,返回人脸框,同时将 人脸信息(图片、人脸框、特征值) 传给人像搜索线程。
+     *
+     * @param object
+     */
+    public synchronized void pushFaceInfo(IFaceInfo object) {
+        mCurFaceInfo = object;
+//        saveImageNV21(object.getImage(), mWidth, mHeight, object.getImageRect(), 100,TimeUtil.longToString(System.currentTimeMillis(), "yyyyMMddHHmmss") + "检测" + ".jpg");
+
+        if (mS_BeforeSearchFace == 0L) {
+            mS_BeforeSearchFace = System.currentTimeMillis();
+        } else {
+            Log.e(TAG, "走到这里,意味着两个线程对于mImageNV21的处理出问题了");
+        }
+    }
+
+
+    /**
+     * sdk 初始化是否成功
+     */
+    public boolean isSdkInit() {
+        return false;
+    }
+
+    /**
+     * 异步提取特征
+     */
+    public void saveToFavemodel(Bitmap bitmap) {
+
+    }
+    public Bitmap cropFace(Bitmap bitmap){
+        return bitmap;
+    }
+
+}

+ 22 - 0
app/src/main/java/com/sunwin/visitorapp/face/FRAbsLoopFactory.java

@@ -0,0 +1,22 @@
+package com.sunwin.visitorapp.face;
+
+import android.content.Context;
+
+public class FRAbsLoopFactory implements FaceDecterFactory {
+
+
+    @Override
+    public FRAbsLoop createFRAblLoop(Context context, int type) {
+        FRAbsLoop frAbsLoop=null;
+        switch (type) {
+            case 0:
+//                frAbsLoop= IdFaceAbsLoop.getInstance();
+                break;
+            case 1:
+                frAbsLoop= SwFaceLoop.getInstance();
+                break;
+        }
+        return frAbsLoop;
+    }
+
+}

+ 12 - 0
app/src/main/java/com/sunwin/visitorapp/face/FaceDecterFactory.java

@@ -0,0 +1,12 @@
+package com.sunwin.visitorapp.face;
+
+import android.content.Context;
+
+/**
+ * @author tianfeng
+ * 算法引擎抽象工厂
+ * create at 2018/9/20 0020 下午 7:16
+ */
+interface FaceDecterFactory {
+    FRAbsLoop createFRAblLoop(Context context, int type);
+}

+ 196 - 0
app/src/main/java/com/sunwin/visitorapp/face/IDCardReaderUtil.java

@@ -0,0 +1,196 @@
+package com.sunwin.visitorapp.face;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+
+import com.sunwin.visitorapp.utils.BitmapUtils;
+import com.sunwin.visitorapp.utils.Constant;
+import com.sunwin.visitorapp.utils.LogUtil;
+import com.sunwin.visitorapp.utils.ToastUtils;
+import com.zkteco.android.IDReader.IDPhotoHelper;
+import com.zkteco.android.IDReader.WLTService;
+import com.zkteco.android.biometric.core.device.ParameterHelper;
+import com.zkteco.android.biometric.core.device.TransportType;
+import com.zkteco.android.biometric.core.utils.LogHelper;
+import com.zkteco.android.biometric.module.idcard.IDCardReader;
+import com.zkteco.android.biometric.module.idcard.IDCardReaderFactory;
+import com.zkteco.android.biometric.module.idcard.exception.IDCardReaderException;
+import com.zkteco.android.biometric.module.idcard.meta.IDCardInfo;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 身份证读取模块
+ */
+public class IDCardReaderUtil implements Runnable {
+
+    private static final String TAG = "IDCardReaderUtil";
+    private final FRAbsLoop mFRAbsLoop;
+    private Context mContext;
+    private Thread childThread;
+    private boolean stopRead;
+    //身份证读卡器模块
+    private static final int VID = 1024;    //IDR VID
+    private static final int PID = 50010;     //IDR PID
+    private IDCardReader idCardReader;
+    private boolean bopen;
+    private boolean bStoped;
+    private CountDownLatch countdownLatch;
+    private String mAuthentticaImagePath;
+
+    private IdCardReadCallback mIdCardReadCallback;
+
+    public IDCardReaderUtil(Context context, IdCardReadCallback callback) {
+        this.mContext = context;
+        countdownLatch = new CountDownLatch(1);
+        mFRAbsLoop = new FRAbsLoopFactory().createFRAblLoop(context, Constant.NumerValue.FRABS_TYPE);
+        mIdCardReadCallback = callback;
+
+    }
+
+    public void startRead() {
+        if (childThread == null) {
+            stopRead = false;//开始后,停止解析标签为false
+            childThread = new Thread(this, "decode");
+            childThread.start();
+        }
+    }
+
+    public void stopRead() {
+        if (childThread != null) {
+            stopRead = true;//子线程调用stop没法停止线程的,我们用了while循环,所以做个标志位,提示跳出while循环
+            childThread = null;
+        }
+    }
+
+    @Override
+    public void run() {
+        processByExtractor();
+    }
+
+    private void processByExtractor() {
+        startIDCardReader();
+    }
+
+    private void startIDCardReader() {
+        // Define output log level
+        LogHelper.setLevel(android.util.Log.ASSERT);
+        // Start fingerprint sensor
+        Map idrparams = new HashMap();
+        idrparams.put(ParameterHelper.PARAM_KEY_VID, VID);
+        idrparams.put(ParameterHelper.PARAM_KEY_PID, PID);
+        idCardReader = IDCardReaderFactory.createIDCardReader(mContext, TransportType.USB, idrparams);
+        Constant.isReConnectUsb = false;
+        startConnect();
+    }
+
+    private void startConnect() {
+        try {
+            if (bopen) {
+                ToastUtils.showToast("设备已连接");
+                return;
+            }
+            idCardReader.open(0);
+            bStoped = false;
+            bopen = true;
+            while (!bStoped) {
+                try {
+                    Thread.sleep(800);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                boolean bSamStatus = false;
+                final IDCardInfo idCardInfo = new IDCardInfo();
+                boolean ret = false;
+                try {
+                    bSamStatus = idCardReader.getStatus(0);
+                } catch (IDCardReaderException e) {
+                    e.printStackTrace();
+                    reConnectIdCardDevice();
+                }
+
+                try {
+                    if (!bSamStatus) {
+                        idCardReader.reset(0);
+                    }
+                    idCardReader.findCard(0);
+                    idCardReader.selectCard(0);
+                    ret = idCardReader.readCard(0, 0, idCardInfo);
+                } catch (IDCardReaderException e) {
+//                    e.printStackTrace();
+                }
+
+                if (ret) {
+
+                    if (idCardInfo.getPhotolength() > 0) {
+
+                        byte[] buf = new byte[WLTService.imgLength];
+                        if (1 == WLTService.wlt2Bmp(idCardInfo.getPhoto(), buf)) {
+                            //保存一张照片
+                            String fileName = idCardInfo.getId() + ".jpg";  //jpeg文件名定义
+                            String dirPath = Constant.RecognitionDir;    //系统路径
+                            File mkDir = new File(dirPath);
+                            if (!mkDir.exists()) {
+                                mkDir.mkdirs();   //目录不存在,则创建
+                            }
+                            mAuthentticaImagePath = dirPath + fileName;
+                            Bitmap bitmap = IDPhotoHelper.Bgr2Bitmap(buf);
+                            BitmapUtils.saveJPGE_After(mContext, bitmap, mAuthentticaImagePath, 100);
+                            mFRAbsLoop.detectFace(new File(mAuthentticaImagePath), FRAbsLoop.DETECTER_FRONT_TYPE);
+                            mFRAbsLoop.idCardInfo = idCardInfo;
+                            if (mContext != null) {
+                                LogUtil.e("detecterView", idCardInfo.getName());
+                                if (mIdCardReadCallback != null) {
+                                    mIdCardReadCallback.onIdNumRead(idCardInfo.getId());
+                                }
+
+                            }
+
+                        }
+                    }
+                }
+
+            }
+            countdownLatch.countDown();
+
+        } catch (IDCardReaderException e) {
+            //失败再次连接
+            LogUtil.e(TAG, "连接失败");
+//            ToastUtils.showToast("连接失败");
+        } finally {
+//            stopConnect();
+        }
+    }
+
+    public void stopConnect() {
+        if (!bopen) {
+            return;
+        }
+        bStoped = true;
+        try {
+            countdownLatch.await(2, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        try {
+            idCardReader.close(0);
+        } catch (IDCardReaderException e) {
+            e.printStackTrace();
+        }
+        bopen = false;
+    }
+
+    private void reConnectIdCardDevice() {
+        stopConnect();
+        if (idCardReader != null) {
+            idCardReader.destroy();
+            idCardReader = null;
+        }
+        startIDCardReader();
+    }
+
+}

+ 37 - 0
app/src/main/java/com/sunwin/visitorapp/face/IFaceInfo.java

@@ -0,0 +1,37 @@
+package com.sunwin.visitorapp.face;
+
+import android.graphics.Rect;
+
+/**
+ * Created by shitianci on 2018/7/16.
+ */
+interface IFaceInfo {
+    /**
+     * 获取图片数据
+     * @return
+     */
+    byte[] getImage();
+
+    /**
+     * 获取人像框
+     * @return
+     */
+    Rect getImageRect();
+
+    /**
+     * 获取人脸特性信息
+     * @return
+     */
+    byte[] getFaceFeature();
+    /**
+     * 获取人脸特性信息
+     * @return
+     */
+    float[] getSwFaceFeature();
+
+    /**
+     * 第三方额外信息
+     * @return
+     */
+    Object getFaceExtends();
+}

+ 7 - 0
app/src/main/java/com/sunwin/visitorapp/face/IdCardReadCallback.java

@@ -0,0 +1,7 @@
+package com.sunwin.visitorapp.face;
+
+import android.graphics.Bitmap;
+
+public interface IdCardReadCallback {
+    void onIdNumRead(String idNum);
+}

+ 515 - 0
app/src/main/java/com/sunwin/visitorapp/face/SwFaceLoop.java

@@ -0,0 +1,515 @@
+package com.sunwin.visitorapp.face;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.graphics.YuvImage;
+import android.os.Environment;
+
+import com.guo.android_extend.java.ExtByteArrayOutputStream;
+import com.nostra13.universalimageloader.core.ImageLoader;
+import com.sunwin.visitorapp.BaseApplication;
+import com.sunwin.visitorapp.model.FacesModel;
+import com.sunwin.visitorapp.model.OpenDoorByFaceNetResultInfo;
+import com.sunwin.visitorapp.model.UserModel;
+import com.sunwin.visitorapp.model.bus.DescEvent;
+import com.sunwin.visitorapp.utils.AppUtil;
+import com.sunwin.visitorapp.utils.BitmapUtils;
+import com.sunwin.visitorapp.utils.Constant;
+import com.sunwin.visitorapp.utils.ConvertUtil;
+import com.sunwin.visitorapp.utils.FileUtil;
+import com.sunwin.visitorapp.utils.LogUtil;
+import com.sunwin.visitorapp.utils.NV21ToBitmap;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+
+import mcv.facepass.FacePassException;
+import mcv.facepass.FacePassHandler;
+import mcv.facepass.types.FacePassCompareResult;
+import mcv.facepass.types.FacePassConfig;
+import mcv.facepass.types.FacePassDetectionResult;
+import mcv.facepass.types.FacePassImage;
+import mcv.facepass.types.FacePassImageType;
+import mcv.facepass.types.FacePassModel;
+import mcv.facepass.types.FacePassPose;
+import mcv.facepass.types.FacePassRCAttribute;
+import mcv.facepass.types.FacePassTrackOptions;
+
+
+public class SwFaceLoop extends FRAbsLoop {
+    private static final String DEBUG_TAG = "SwFaceLoop";
+    public static boolean shouldStop = false;
+    private static SwFaceLoop mSwFaceLoop;
+    private FacePassHandler mFacePassHandler;
+    private boolean isLocalGroupExist;
+    private NV21ToBitmap nV21ToBitmap;
+    private FeedFrameThread feedFrameThread;
+    private boolean isRecognized;
+    private ArrayBlockingQueue<CameraPreviewData> mFeedFrameQueue;
+    private ArrayBlockingQueue<DetectResult> mDetectResultQueue;
+    private int recognizeTime;
+    private RecognizeThread mRecognizeThread;
+
+    private String group_name = "facepass";
+
+    public SwFaceLoop(Context context) {
+        initialEngine(context);
+    }
+
+    public static SwFaceLoop getInstance() {
+        if (mSwFaceLoop == null) {
+            synchronized (SwFaceLoop.class) {
+                if (mSwFaceLoop == null) {
+                    mSwFaceLoop = new SwFaceLoop(BaseApplication.getInstance());
+                }
+            }
+        }
+        return mSwFaceLoop;
+    }
+
+    @Override
+    public void initialEngine(Context context) {
+        LogUtil.e(TAG, "-------------initialEngine-------------");
+        mFeedFrameQueue = new ArrayBlockingQueue<CameraPreviewData>(1);
+        mDetectResultQueue = new ArrayBlockingQueue<DetectResult>(5);
+        File mkDir = new File(Constant.SWBINDIR);
+        //第一次开启应用,将asserts目录下models文件复制到设备上
+        if (!mkDir.exists()) {
+            FileUtil.copyFilesFromAssets(context, "models", Constant.SWBINDIR);
+        }
+        nV21ToBitmap = new NV21ToBitmap(context);
+        initFacePassSDK();
+        initFaceHandler();
+
+        mRecognizeThread = new RecognizeThread();
+        mRecognizeThread.start();
+        feedFrameThread = new FeedFrameThread();
+        feedFrameThread.start();
+    }
+
+    private void initFacePassSDK() {
+        FacePassHandler.initSDK(BaseApplication.getInstance());
+        LogUtil.d(TAG, FacePassHandler.getVersion());
+    }
+
+    private void initFaceHandler() {
+
+        new Thread() {
+            @Override
+            public void run() {
+                while (true) {
+                    LogUtil.e("FacePassHandler", "" + FacePassHandler.isAuthorized());
+                    while (FacePassHandler.isAvailable()) {
+                        LogUtil.d(DEBUG_TAG, "start to build FacePassHandler");
+                        FacePassConfig config;
+                        try {
+                            /* 填入所需要的配置 */
+                            config = new FacePassConfig();
+                            config.poseBlurModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "attr.pose_blur.align.av200.190630.bin");
+                            //单目使用CPU rgb活体模型
+                            config.livenessModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "liveness.CPU.rgb.int8.E.bin");
+                            //双目使用CPU rgbir活体模型
+                            //config.rgbIrLivenessModel = FacePassModel.initModel(application.getAssets().getAssets(), "liveness.CPU.rgbir.int8.E.bin");
+                            //当单目或者双目有一个使用GPU活体模型时,请设置livenessGPUCache
+                            //config.livenessGPUCache = FacePassModel.initModel(application.getAssets().getAssets(), "liveness.GPU.AlgoPolicy.E.cache");
+                            config.searchModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "feat2.arm.H.v1.0_1core.bin");
+                            config.detectModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "detector.arm.E.bin");
+                            config.detectRectModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "detector_rect.arm.E.bin");
+                            config.landmarkModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "pf.lmk.arm.D.bin");
+
+                            config.rcAttributeModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "attr.RC.gray.12M.arm.200229.bin");
+
+
+//                            config.smileModel = FacePassModel.initModel(application.getAssets(), "attr.blur.align.gray.general.mgf29.0.1.1.181229.bin");
+//                            config.ageGenderModel = FacePassModel.initModel(application.getAssets(), "age_gender.v2.bin");
+                            //如果不需要表情和年龄性别功能,smileModel和ageGenderModel可以为null
+                            //config.smileModel = null;
+                            //config.ageGenderModel = null;
+
+                            config.searchThreshold = Constant.COMPARSION_VALUE;
+                            config.livenessThreshold = 50f;// TODO: 2021/12/22   60f
+                            config.livenessEnabled = true;
+//                            config.rgbIrLivenessEnabled = false;
+                            config.faceMinThreshold = 100;
+                            config.poseThreshold = new FacePassPose(30f, 30f, 30f);
+                            config.blurThreshold = 0.2f;
+                            config.lowBrightnessThreshold = 50f;// TODO: 2021/12/22 70f
+                            config.highBrightnessThreshold = 210f;
+                            config.brightnessSTDThreshold = 90f;// TODO: 2021/12/22 60
+                            config.retryCount = 2;
+                            config.smileEnabled = false;
+                            config.maxFaceEnabled = true;
+                            config.faceMinThreshold = 20;
+                            config.fileRootPath = Environment.getExternalStorageDirectory().getAbsolutePath();
+
+                            /* 创建SDK实例 */
+                            mFacePassHandler = new FacePassHandler(config);
+                            int faceMinThreshold2 = 20;
+                            float blurThreshold2 = 0.6f;
+                            float lowBrightnessThreshold2 = 70f;
+                            float highBrightnessThreshold2 = 210f;
+                            float brightnessSTDThreshold2 = 60f;
+                            FacePassConfig config1 = new FacePassConfig(faceMinThreshold2, 25f, 25f, 25f, blurThreshold2,
+                                    lowBrightnessThreshold2, highBrightnessThreshold2, brightnessSTDThreshold2);
+                            boolean is = mFacePassHandler.setAddFaceConfig(config1);
+                            LogUtil.d(DEBUG_TAG, "builid FacePassHandler success");
+                            checkGroup();
+
+
+                        } catch (FacePassException e) {
+                            e.printStackTrace();
+                            LogUtil.d(DEBUG_TAG, "FacePassHandler is null");
+                            return;
+                        }
+                        return;
+                    }
+
+                }
+            }
+        }.start();
+    }
+
+//    private void initFaceHandler() {
+//
+//        new Thread() {
+//            @Override
+//            public void run() {
+//                while (true) {
+//                    LogUtil.e(DEBUG_TAG, "FacePassHandler.isAuthorized()" + FacePassHandler.isAuthorized());
+//                    while (FacePassHandler.isAvailable()) {
+//                        LogUtil.d(DEBUG_TAG, "start to build FacePassHandler");
+//                        FacePassConfig config;
+//                        try {
+//                            /* 填入所需要的配置 */
+//                            config = new FacePassConfig();
+//                            config.poseBlurModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "attr.pose_blur.align.av200.190630.bin");
+//                            config.livenessModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "liveness.CPU.rgb.int8.E.bin");
+//                            config.searchModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "feat2.arm.H.v1.0_1core.bin");
+//                            config.detectModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "detector.arm.E.bin");
+//                            config.detectRectModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "detector_rect.arm.E.bin");
+//                            config.landmarkModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "pf.lmk.arm.D.bin");
+//
+//                            config.rcAttributeModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "attr.RC.gray.12M.arm.200229.bin");
+//
+//                            config.rcAttributeEnabled = true;
+//                            config.searchThreshold = Constant.COMPARSION_VALUE;
+//                            config.livenessThreshold = 60f;
+//                            if (Constant.NumerValue.cameraNums > 1) {
+//                                config.livenessEnabled = false;
+//                                //用于人脸红外活体检测的模型
+//                                config.rgbIrLivenessEnabled = true;
+//                                config.rgbIrLivenessModel = FacePassModel.initModel(BaseApplication.getInstance().getAssets(), "liveness.CPU.rgbir.int8.E.bin");
+//                            } else {
+//                                config.livenessEnabled = true;
+//                                //用于人脸红外活体检测的模型
+//                                config.rgbIrLivenessEnabled = false;
+//                            }
+//
+//                            config.faceMinThreshold = Constant.FACE_MIN_FACE_VALUE;
+//                            config.poseThreshold = new FacePassPose(30f, 30f, 30f);
+//                            config.blurThreshold = 0.8f;
+//                            config.lowBrightnessThreshold = 70f;
+//                            config.highBrightnessThreshold = 210f;
+//                            config.brightnessSTDThreshold = 80f;
+//                            config.retryCount = 1;
+//                            config.smileEnabled = false;
+//                            config.maxFaceEnabled = true;
+////                            config.rotation = carmeraRotate;
+//                            config.fileRootPath = Environment.getExternalStorageDirectory().getAbsolutePath();
+//
+//                            /* 创建SDK实例 */
+//                            mFacePassHandler = new FacePassHandler(config);
+//                            FacePassConfig addFaceConfig = mFacePassHandler.getAddFaceConfig();
+//                            addFaceConfig.blurThreshold = 0.8f;
+//                            addFaceConfig.faceMinThreshold = Constant.FACE_MIN_FACE_VALUE;
+//                            boolean is = mFacePassHandler.setAddFaceConfig(addFaceConfig);
+//                            LogUtil.d(DEBUG_TAG, "builid FacePassHandler success");
+//                            checkGroup();
+//
+//                        } catch (FacePassException e) {
+//                            e.printStackTrace();
+//                            LogUtil.d(DEBUG_TAG, "FacePassHandler is null");
+//                            return;
+//                        }
+//                        return;
+//                    }
+//                    try {
+//                        Thread.sleep(400);
+//                    } catch (InterruptedException e) {
+//                        e.printStackTrace();
+//                    }
+//                }
+//            }
+//        }.start();
+//    }
+
+    private void checkGroup() {
+        if (mFacePassHandler == null) {
+            return;
+        }
+        String[] localGroups = new String[0];
+        try {
+            localGroups = mFacePassHandler.getLocalGroups();
+        } catch (FacePassException e) {
+            e.printStackTrace();
+        }
+        isLocalGroupExist = false;
+        if (localGroups == null || localGroups.length == 0) {
+            try {
+                boolean isSuccess = mFacePassHandler.createLocalGroup(group_name);
+                LogUtil.d(DEBUG_TAG, "创建库 " + AppUtil.getPackageName() + ":" + isSuccess);
+            } catch (FacePassException e) {
+                e.printStackTrace();
+            }
+        }
+        for (String group : localGroups) {
+            if (group_name.equals(group)) {
+                isLocalGroupExist = true;
+            }
+        }
+    }
+
+    @Override
+    public List<Rect> faceDetection(Bitmap bitmap, int type) {
+        return null;
+    }
+
+    @Override
+    public boolean detectFace(File file, int type) {
+        return true;
+    }
+
+    @Override
+    public void saveFace(UserModel userModel) {
+    }
+
+    @Override
+    public void uninitialEngine() {
+
+    }
+
+    @Override
+    public Rect[] onPreview(byte[] data, int width, int height, int format, long timestamp) {
+
+        mFeedFrameQueue.offer(new CameraPreviewData(data, width, height, 0, true));
+
+        return super.onPreview(data, width, height, format, timestamp);
+    }
+
+    @Override
+    public void searchFace() {
+        if (shouldStop) {
+            return;
+        }
+        oneDetect();
+
+    }
+
+    private void oneDetect() {
+        ///单目活体
+
+        CameraPreviewData cameraPreviewData = null;
+        try {
+            //从阻塞列表中取出照片
+            cameraPreviewData = mFeedFrameQueue.take();
+            if (mFacePassHandler == null) {
+                return;
+            }
+            /* 将相机预览帧转成SDK算法所需帧的格式 FacePassImage */
+            long startTime = System.currentTimeMillis(); //起始时间
+
+            FacePassImage image;
+            try {
+                image = new FacePassImage(cameraPreviewData.nv21Data, cameraPreviewData.width, cameraPreviewData.height, 0, FacePassImageType.NV21);
+            } catch (FacePassException e) {
+                e.printStackTrace();
+                return;
+            }
+            /* 将每一帧FacePassImage 送入SDK算法, 并得到返回结果 */
+            FacePassDetectionResult detectionResult = null;
+            try {
+                detectionResult = mFacePassHandler.feedFrame(image);
+            } catch (FacePassException e) {
+                e.printStackTrace();
+            }
+            if (detectionResult == null || detectionResult.faceList.length == 0) {
+                /* 当前帧没有检出人脸 */
+                EventBus.getDefault().post(new DescEvent().setDesc(DescEvent.DESC_FR_TIP));
+//                EventBus.getDefault().post(new BusEvent("ksfrabsloop", "clear"));
+            } else {
+                /* 将识别到的人脸在预览界面中圈出,并在上方显示人脸位置及角度信息 */
+                EventBus.getDefault().post(new DescEvent().setDesc(DescEvent.DESC_FR_ING));
+            }
+            if (detectionResult != null && detectionResult.message.length != 0) {
+//                Log.e(DEBUG_TAG, "mDetectResultQueue.offer");
+                FacePassTrackOptions[] trackOpts = new FacePassTrackOptions[detectionResult.images.length];
+                for (int i = 0; i < detectionResult.images.length; ++i) {
+                    if (detectionResult.images[i].rcAttr.respiratorType != FacePassRCAttribute.FacePassRespiratorType.INVALID
+                            && detectionResult.images[i].rcAttr.respiratorType != FacePassRCAttribute.FacePassRespiratorType.NO_RESPIRATOR) {
+                        float searchThreshold = 60f;
+                        float livenessThreshold = -1.0f; // -1.0f will not change the liveness threshold
+                        trackOpts[i] = new FacePassTrackOptions(detectionResult.images[i].trackId, searchThreshold, livenessThreshold);
+                    }
+                    LogUtil.d(DEBUG_TAG, String.format("rc attribute in FacePassImage, hairType: 0x%x beardType: 0x%x hatType: 0x%x respiratorType: 0x%x glassesType: 0x%x skinColorType: 0x%x",
+                            detectionResult.images[i].rcAttr.hairType.ordinal(),
+                            detectionResult.images[i].rcAttr.beardType.ordinal(),
+                            detectionResult.images[i].rcAttr.hatType.ordinal(),
+                            detectionResult.images[i].rcAttr.respiratorType.ordinal(),
+                            detectionResult.images[i].rcAttr.glassesType.ordinal(),
+                            detectionResult.images[i].rcAttr.skinColorType.ordinal()));
+                }
+                DetectResult detectResult = new DetectResult();
+                detectResult.setCameraPreviewData(cameraPreviewData);
+                detectResult.setDetectResult(detectionResult.message);
+                mDetectResultQueue.offer(detectResult);
+            } else {
+//                Log.e(DEBUG_TAG, "null");
+            }
+            long endTime = System.currentTimeMillis(); //结束时间
+            long runTime = endTime - startTime;
+//            Log.i("]time", String.format("feedfream %d ms", runTime));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private class FeedFrameThread extends Thread {
+        boolean isInterrupt;
+
+        @Override
+        public void run() {
+            while (true) {
+                try {
+                    if (isTimeWrong) {
+                        Thread.sleep(1000);
+                    }
+                    if (isVerifying) {
+                        Thread.sleep(100);
+                        continue;
+                    }
+
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                searchFace();
+            }
+        }
+
+        @Override
+        public void interrupt() {
+            isInterrupt = true;
+
+            super.interrupt();
+        }
+    }
+
+    private class RecognizeThread extends Thread {
+
+        boolean isInterrupt;
+
+        @Override
+        public void run() {
+            while (true) {
+                try {
+                    isRecognized = true;
+                    LogUtil.d(DEBUG_TAG, "take");
+                    DetectResult detectResult = mDetectResultQueue.take();
+                    byte[] detectionResult = detectResult.getDetectResult();
+
+                    if (isLocalGroupExist) {
+
+                        if (idCardInfo != null) {
+                            idCardRecognite(detectResult);
+
+                        } else {
+//                            userRecognite(detectResult, detectionResult);
+                        }
+                    } else {
+                        isLocalGroupExist = mFacePassHandler.createLocalGroup(group_name);
+                        LogUtil.d(DEBUG_TAG, "not isLocalGroupExist");
+                    }
+                } catch (Exception e) {
+                    LogUtil.e(DEBUG_TAG, e.toString());
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        @Override
+        public void interrupt() {
+            isInterrupt = true;
+            super.interrupt();
+        }
+    }
+
+    private void idCardRecognite(DetectResult detectResult) {
+
+        OpenDoorByFaceNetResultInfo openDoorByFaceNetResultInfo = new OpenDoorByFaceNetResultInfo();
+        openDoorByFaceNetResultInfo.setCode(-1);
+        if (idCardInfo.getPhoto() != null) {
+            String fileName = idCardInfo.getId() + ".jpg";  //jpeg文件名定义
+            String dirPath = Constant.RecognitionDir;    //系统路径
+            String path = "file://" + dirPath + fileName;
+            String currentFacePath;
+            Bitmap idBitmap = ImageLoader.getInstance().loadImageSync(path);
+            CameraPreviewData cameraPreviewData = detectResult.getCameraPreviewData();
+            final YuvImage yuv = new YuvImage(cameraPreviewData.nv21Data, ImageFormat.NV21, cameraPreviewData.width, cameraPreviewData.height, null);
+            ExtByteArrayOutputStream ops = new ExtByteArrayOutputStream();
+            FacePassCompareResult facePassCompareResult1 = null;
+            try {
+                yuv.compressToJpeg(new Rect(0, 0, cameraPreviewData.width, cameraPreviewData.height), 100, ops);
+                Bitmap bitmap = BitmapFactory.decodeByteArray(ops.getByteArray(), 0, ops.getByteArray().length);
+                Bitmap currentPersonBitmap = BitmapUtils.rotateBitmap(bitmap, 0);
+                if (currentPersonBitmap == null) {
+                    return;
+                }
+                ops.close();
+                long startTimer = System.currentTimeMillis();
+                LogUtil.e("step", "4");
+
+                if (currentPersonBitmap != null && idBitmap != null) {
+                    facePassCompareResult1 = mFacePassHandler.compare(currentPersonBitmap, idBitmap, false);
+
+                }
+                LogUtil.e("step", "5");
+                /**
+                 * 身份证照片和人脸识别照片比对
+                 * */
+                if (facePassCompareResult1 != null && facePassCompareResult1.score > Constant.FACE_ID_COMPARE_VALUE) {
+                    LogUtil.e(DEBUG_TAG, "人证识别时间:" + (System.currentTimeMillis() - startTimer) + ",分数=" + facePassCompareResult1.score);
+                    currentFacePath = BitmapUtils.transToFileFromNv21("current" + idCardInfo.getId(), cameraPreviewData.nv21Data, cameraPreviewData.width, cameraPreviewData.height, new Rect(0, 0, cameraPreviewData.width, cameraPreviewData.height));
+                    LogUtil.e("currentFacePath", currentFacePath);
+                    openDoorByFaceNetResultInfo.setCode(0);
+                    openDoorByFaceNetResultInfo.setSimilarity(facePassCompareResult1.score);
+                    openDoorByFaceNetResultInfo.setCurrentFace(currentFacePath);
+                    openDoorByFaceNetResultInfo.setPerson_code(idCardInfo.getId());
+                    openDoorByFaceNetResultInfo.setPerson_name(idCardInfo.getName());
+                    ArrayList<FacesModel> arrayList = new ArrayList<>();
+                    FacesModel facesModel = new FacesModel();
+                    facesModel.setFace_image(ConvertUtil.bitmapToBase64(idBitmap));
+                    arrayList.add(facesModel);
+                    openDoorByFaceNetResultInfo.setFaces(arrayList);
+                    EventBus.getDefault().post(new DescEvent().setDesc(DescEvent.DESC_FR_SUCC).setOpenDoorByFaceNetResultInfo(openDoorByFaceNetResultInfo));
+                    recognizeTime = 0;
+                    LogUtil.e("step", "6");
+                } else {
+                    LogUtil.e(TAG, "人证识别时间:"+recognizeTime);
+                    recognizeTime++;
+                    if (recognizeTime == Constant.COMPARSION_FREQUENCY) {
+                        EventBus.getDefault().post(new DescEvent().setDesc(DescEvent.DESC_FR_NO_BODY));
+                        idCardInfo = null;
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}

+ 19 - 0
app/src/main/java/com/sunwin/visitorapp/model/BaseModel.java

@@ -0,0 +1,19 @@
+package com.sunwin.visitorapp.model;
+
+import com.google.gson.Gson;
+
+import java.io.Serializable;
+
+public class BaseModel implements Serializable {
+
+    private static Gson gson = new Gson();
+
+    @Override
+    public String toString() {
+        return getGson().toJson(this);
+    }
+
+    public static Gson getGson() {
+        return gson;
+    }
+}

+ 12 - 0
app/src/main/java/com/sunwin/visitorapp/model/FacesModel.java

@@ -0,0 +1,12 @@
+package com.sunwin.visitorapp.model;
+
+public class FacesModel extends BaseModel{ private String face_image;
+
+    public String getFace_image() {
+        return face_image;
+    }
+
+    public void setFace_image(String face_image) {
+        this.face_image = face_image;
+    }
+}

+ 69 - 0
app/src/main/java/com/sunwin/visitorapp/model/NetResultInfo.java

@@ -0,0 +1,69 @@
+package com.sunwin.visitorapp.model;
+
+public class NetResultInfo extends BaseModel {
+    //由于机务段的接口成功值为1,此处需要特殊适配。
+    public final static int RETURN_CODE_000000 = 0;    //成功
+    public final static int NON_USER = 110001;//不是用户
+    public final static int RETURN_CODE_999999 = 999999;    //其他错误	提示服务端返回的描述信息
+
+
+    public int getSwSpecCode() {
+        return swSpecCode;
+    }
+
+    private int swSpecCode = RETURN_CODE_999999;
+    private String respDesc = "其他错误";
+
+
+    public void setSwSpecCode(int swSpecCode) {
+        this.swSpecCode = swSpecCode;
+    }
+
+    public String getRespDesc() {
+        if (swSpecCode == RETURN_CODE_999999) {
+            //兼容机务段项目
+            return getReturnDesc();
+        }
+        return respDesc;
+    }
+
+    public void setRespDesc(String respDesc) {
+        this.respDesc = respDesc;
+    }
+
+
+    public static NetResultInfo getMockData() {
+        NetResultInfo result = new NetResultInfo();
+        result.setSwSpecCode(RETURN_CODE_000000);
+        result.setRespDesc("OK");
+        return result;
+    }
+
+    /**
+     * !!! 以下代码为兼容机务段项目做得适配,其它项目不要采用这样的配置
+     */
+    @Deprecated
+    private int returnCode = RETURN_CODE_999999;
+    @Deprecated
+    private String returnDesc = "其他错误";
+
+    @Deprecated
+    public String getReturnDesc() {
+        return returnDesc;
+    }
+
+    @Deprecated
+    public void setReturnDesc(String mReturnDesc) {
+        returnDesc = mReturnDesc;
+    }
+
+    @Deprecated
+    public int getReturnCode() {
+        return returnCode;
+    }
+
+    @Deprecated
+    public void setReturnCode(int mReturnCode) {
+        returnCode = mReturnCode;
+    }
+}

+ 147 - 0
app/src/main/java/com/sunwin/visitorapp/model/OpenDoorByFaceNetResultInfo.java

@@ -0,0 +1,147 @@
+package com.sunwin.visitorapp.model;
+
+import java.util.List;
+
+public class OpenDoorByFaceNetResultInfo extends NetResultInfo{
+
+    private int code;
+    private String message = "";
+    private String person_name = "";
+    private String person_code = "";
+    private String person_id_card = "";
+    private String currentFace ="";
+    private double similarity;
+
+    private List<FacesModel> faces;
+
+    public OpenDoorByFaceNetResultInfo(OpenDoorByFaceNetResultInfo openDoorByFaceNetResultInfo) {
+        this.code = openDoorByFaceNetResultInfo.code;
+        this.message = openDoorByFaceNetResultInfo.message;
+        this.person_name = openDoorByFaceNetResultInfo.person_name;
+        this.person_code = openDoorByFaceNetResultInfo.person_code;
+        this.similarity = openDoorByFaceNetResultInfo.similarity;
+    }
+
+    public OpenDoorByFaceNetResultInfo() {
+
+    }
+
+    public String getPerson_id_card() {
+        return person_id_card;
+    }
+
+    public void setPerson_id_card(String person_id_card) {
+        this.person_id_card = person_id_card;
+    }
+
+    public String getCurrentFace() {
+        return currentFace;
+    }
+
+    public void setCurrentFace(String currentFace) {
+        this.currentFace = currentFace;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getPerson_name() {
+        return person_name;
+    }
+
+    public void setPerson_name(String person_name) {
+        this.person_name = person_name;
+    }
+
+    public String getPerson_code() {
+        return person_code;
+    }
+
+    public void setPerson_code(String person_code) {
+        this.person_code = person_code;
+    }
+
+    public double getSimilarity() {
+        return similarity;
+    }
+
+    public void setSimilarity(double similarity) {
+        this.similarity = similarity;
+    }
+
+    public List<FacesModel> getFaces() {
+        return faces;
+    }
+
+    public void setFaces(List<FacesModel> faces) {
+        this.faces = faces;
+    }
+
+    @Override
+    public String toString() {
+        OpenDoorByFaceNetResultInfo result = new OpenDoorByFaceNetResultInfo(this);
+        return getGson().toJson(result);
+    }
+
+  /*  public static class Request extends BaseModel implements HttpParamModel {
+
+        private String face_image;
+        private String sn;
+        private String card_id;
+        private String card_name;
+        private String client_id;
+
+        public String getCard_name() {
+            return card_name;
+        }
+
+        public void setCard_name(String card_name) {
+            this.card_name = card_name;
+        }
+
+        public String getClient_id() {
+            return client_id;
+        }
+
+        public void setClient_id(String client_id) {
+            this.client_id = client_id;
+        }
+
+        public String getCard_id() {
+            return card_id;
+        }
+
+        public void setCard_id(String card_id) {
+            this.card_id = card_id;
+        }
+
+        public String getFace_image() {
+            return face_image;
+        }
+
+        public void setFace_image(String face_image) {
+            this.face_image = face_image;
+        }
+
+        public String getSn() {
+            return sn;
+        }
+
+        public void setSn(String sn) {
+            this.sn = sn;
+        }
+    }*/
+}

+ 4 - 0
app/src/main/java/com/sunwin/visitorapp/model/UserModel.java

@@ -0,0 +1,4 @@
+package com.sunwin.visitorapp.model;
+
+public class UserModel {
+}

+ 37 - 0
app/src/main/java/com/sunwin/visitorapp/model/bus/DescEvent.java

@@ -0,0 +1,37 @@
+package com.sunwin.visitorapp.model.bus;
+
+import com.sunwin.visitorapp.model.BaseModel;
+import com.sunwin.visitorapp.model.OpenDoorByFaceNetResultInfo;
+
+public class DescEvent extends BaseModel {
+    public static final String DESC_FR_TIP = "请注视相机"; //识别失败的时候提示
+    public static  final String DESC_FR_TIP_2 = "请刷身份证";
+    public static final String DESC_FR_ING = "正在识别中...";
+    public static final String DESC_FR_SUCC = "识别成功";
+    public static final String DESC_FR_NET_ERROR = "网络异常";
+    public static final String DESC_DEAL_NEW_DATA = "识别模块正在处理更新的数据";
+    public static final String DESC_FR_NO_BODY = "请重新识别";
+    public static final String DESC_NEED_FLUSH_CARD = "需刷卡校验";
+    public static final String DESC_FR_FROCUSE_EYE = "请正面注视相机";
+    private String desc = "";
+    private OpenDoorByFaceNetResultInfo openDoorByFaceNetResultInfo;
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public DescEvent setDesc(String desc) {
+        this.desc = desc;
+        return this;
+    }
+//
+    public OpenDoorByFaceNetResultInfo getOpenDoorByFaceNetResultInfo() {
+        return openDoorByFaceNetResultInfo;
+    }
+
+    public DescEvent setOpenDoorByFaceNetResultInfo(OpenDoorByFaceNetResultInfo openDoorByFaceNetResultInfo) {
+        this.openDoorByFaceNetResultInfo = openDoorByFaceNetResultInfo;
+        return this;
+    }
+
+}

+ 9 - 0
app/src/main/java/com/sunwin/visitorapp/utils/AppUtil.java

@@ -0,0 +1,9 @@
+package com.sunwin.visitorapp.utils;
+
+import com.sunwin.visitorapp.BaseApplication;
+
+public class AppUtil {
+    public static String getPackageName(){
+        return BaseApplication.getInstance().getPackageName();
+    }
+}

+ 1596 - 0
app/src/main/java/com/sunwin/visitorapp/utils/BitmapUtils.java

@@ -0,0 +1,1596 @@
+package com.sunwin.visitorapp.utils;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.ImageFormat;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader.TileMode;
+import android.graphics.YuvImage;
+import android.graphics.drawable.Drawable;
+import android.media.ExifInterface;
+import android.media.MediaScannerConnection;
+import android.media.ThumbnailUtils;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+import android.view.View;
+
+import com.guo.android_extend.java.ExtByteArrayOutputStream;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.text.NumberFormat;
+import java.util.List;
+
+
+
+import static android.graphics.BitmapFactory.decodeResource;
+
+/**
+ * Created by MZIA(527633405@qq.com) on 2016/1/15 0015 11:12
+ * Bitmap操作工具类
+ */
+public class BitmapUtils {
+
+    /**
+     * 将bitmap转换成字节数组形式
+     *
+     * @param bitmap 需要转换的bitmap对象
+     * @return bitmap的字节数组
+     */
+    public static byte[] Bitmap2Bytes(Bitmap bitmap) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
+        return baos.toByteArray();
+    }
+
+    /**
+     * 把字节数组保存为一个文件
+     *
+     * @param bytes      需要保存的字节数组
+     * @param outputFile 保存的文件路径
+     * @return 保存后的文件
+     */
+    public static File getFileFromBytes(byte[] bytes, String outputFile) {
+        BufferedOutputStream stream = null;
+        File file = null;
+        try {
+            file = new File(outputFile);
+            FileOutputStream fos = new FileOutputStream(file);
+            stream = new BufferedOutputStream(fos);
+            stream.write(bytes);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return file;
+    }
+
+    /**
+     * 通过图片资源名称获取图片资源id(扩展 : 比如想根据资源名称获取layout下的文件id,可以将drawable替换成layout)
+     *
+     * @param activity Activity
+     * @param iconName icon名称,不包含后缀
+     * @return 图片资源id
+     */
+    public static int getResIDByResName(Activity activity, String iconName) {
+        Resources resources = activity.getResources();
+        return resources.getIdentifier(activity.getPackageName()
+                + ":drawable/" + iconName, null, null);
+    }
+
+    /**
+     * 从字节数组中解析出bitmap
+     *
+     * @param data 图片的字节数组
+     * @return 从字节数组中解析出的bitmap
+     */
+    public static Bitmap decodeBitmapFromByte(byte[] data) {
+        return BitmapFactory.decodeByteArray(data, 0, data.length);
+    }
+
+    /**
+     * 图片缩放
+     *
+     * @param filePath 图片路径
+     * @param w        指定的宽
+     * @param h        指定的高
+     * @return 缩放后的图片
+     */
+    public static Bitmap scaleBitmap(String filePath, int w, int h) {
+        Options options = new Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(filePath, options);
+        options.inJustDecodeBounds = false;
+        int width = (int) Math.ceil(options.outWidth / (float) w);
+        int height = (int) Math.ceil(options.outHeight / (float) h);
+        if (width > 1 && height > 1) {
+            if (height > width) {
+                options.inSampleSize = height;
+            } else {
+                options.inSampleSize = width;
+            }
+        }
+        return BitmapFactory.decodeFile(filePath, options);
+    }
+
+    public static Bitmap scaleBitmap(Bitmap bitmap, int w, int h) {
+        return Bitmap.createScaledBitmap(bitmap, w, h, true);
+    }
+
+    /**
+     * 保存图片到文件
+     *
+     * @param bitmap 需要保存的图片
+     * @param file   图片保存的文件
+     * @return 是否保存成功
+     */
+    public static boolean saveBitmpa2File(Bitmap bitmap, File file) {
+        try {
+            FileOutputStream fos = new FileOutputStream(file);
+            if (bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)) {
+                fos.flush();
+                fos.close();
+            }
+            return true;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    /**
+     * 从文件中获取图片
+     *
+     * @param file 目标文件
+     * @return 获取到的图片
+     * @throws FileNotFoundException 没有找到文件异常
+     */
+    public static Bitmap getBitmapFromFile(File file) throws FileNotFoundException {
+        if (file == null)
+            return null;
+        Options options = new Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+        options.inJustDecodeBounds = false;
+
+        int rate = (int) (options.outHeight / (float) options.outWidth);
+        if (rate <= 0) {
+            rate = 1;
+        }
+        options.inSampleSize = rate;
+        options.inPurgeable = true;
+        options.inInputShareable = true;
+        return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+    }
+
+    /**
+     * @param view View
+     * @return Bitmap
+     */
+    public static Bitmap getBitmap(View view) {
+        view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
+        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+        view.buildDrawingCache();
+        return view.getDrawingCache();
+    }
+
+    /**
+     * 从网络url中获得bitmap
+     *
+     * @param url 图片网络地址
+     * @return bitmap
+     */
+    public static Bitmap getBitmapFromUrl(String url) {
+        InputStream is = null;
+        try {
+            URL u = new URL(url);
+            HttpURLConnection conn = (HttpURLConnection) u.openConnection();
+            conn.setRequestMethod("GET");
+            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
+                is = conn.getInputStream();
+                Bitmap bitmap = BitmapFactory.decodeStream(is);
+                is.close();
+                conn.disconnect();
+                return bitmap;
+            } else {
+                conn.disconnect();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (is != null) {
+                    is.close();
+                }
+            } catch (IOException e1) {
+                e1.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 从网络url中获得bitmap
+     *
+     * @param url    图片网络地址
+     * @param width  指定的宽
+     * @param height 指定的高
+     * @return 获取到的bitmap
+     */
+    public static Bitmap getBitmapFromUrl(String url, int width, int height) {
+        Bitmap bitmap = getBitmapFromUrl(url);
+        return scaledBitmap(bitmap, width, height);
+    }
+
+    /**
+     * 获取缩放的位置
+     *
+     * @param bitmap    需要被缩放的位置
+     * @param dstWidth  缩放的预期宽度
+     * @param dstHeight 缩放的预期高度
+     * @return 缩放后的位置
+     */
+    public static Bitmap scaledBitmap(Bitmap bitmap, int dstWidth, int dstHeight) {
+        if (bitmap == null)
+            return null;
+        int h = bitmap.getHeight();
+        int w = bitmap.getWidth();
+
+        if (w > h) {
+            if (w > dstWidth || h > dstHeight) {
+                float scaleWidth = ((float) dstWidth) / w;
+                float scaleHeight = ((float) dstHeight) / h;
+                float scale = Math.min(scaleWidth, scaleHeight);
+                Matrix matrix = new Matrix();
+                matrix.postScale(scale, scale);
+                Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h,
+                        matrix, true);
+                if (bitmap != newBitmap) {
+                    bitmap.recycle();
+                }
+                return newBitmap;
+            } else {
+                return bitmap;
+            }
+        } else {
+            if (w > dstWidth || h > dstHeight) {
+                float scaleWidth = ((float) dstHeight) / w;
+                float scaleHeight = ((float) dstWidth) / h;
+                float scale = Math.min(scaleWidth, scaleHeight);
+                Matrix matrix = new Matrix();
+                matrix.postScale(scale, scale);
+                Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h,
+                        matrix, true);
+                if (bitmap != newBitmap) {
+                    bitmap.recycle();
+                    bitmap = null;
+                }
+                return newBitmap;
+            } else {
+                return bitmap;
+            }
+        }
+
+    }
+
+    /**
+     * 返回圆角图片
+     *
+     * @param bitmap 需要转换的位图
+     * @param pixels 圆角半径
+     * @return 转换后的bitmap
+     **/
+    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {
+        if (bitmap == null) {
+            return null;
+        }
+        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
+                bitmap.getHeight(), Config.ARGB_8888);
+        Canvas canvas = new Canvas(output);
+
+        final int color = 0xff424242;
+        final Paint paint = new Paint();
+        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        final RectF rectF = new RectF(rect);
+        final float roundPx = pixels;
+
+        paint.setAntiAlias(true);
+        canvas.drawARGB(0, 0, 0, 0);
+        paint.setColor(color);
+        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
+
+        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+        canvas.drawBitmap(bitmap, rect, rect, paint);
+
+        return output;
+    }
+
+    /**
+     * 第一种旋转图片的方法
+     *
+     * @param bitmap 需要被旋转的图片
+     * @param degree 旋转的角度
+     * @return 旋转后的图片
+     */
+    public static Bitmap rotateBitmap1(Bitmap bitmap, int degree) {
+        if (!(degree != 0 && bitmap != null)) {
+            return bitmap;
+        }
+        Matrix matrix = new Matrix();
+        matrix.postRotate(degree);
+        int width = bitmap.getWidth();
+        int height = bitmap.getHeight();
+        return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
+    }
+
+    /**
+     * 第二种旋转图片的方法
+     *
+     * @param bitmap 需要被旋转的图片
+     * @param degree 旋转的角度
+     * @return 旋转后的图片
+     */
+    public static Bitmap rotateBitmap2(Bitmap bitmap, int degree) {
+        if (degree != 0 && bitmap != null) {
+            Matrix matrix = new Matrix();
+            matrix.setRotate(degree, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
+            try {
+                Bitmap b = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
+                        bitmap.getHeight(), matrix, true);
+                if (bitmap != b) {
+                    bitmap.recycle();
+                }
+            } catch (OutOfMemoryError ex) {
+                ex.printStackTrace();
+            }
+        }
+        return bitmap;
+    }
+
+    public static Bitmap createReflectedBitmap(Bitmap srcBitmap) {
+        if (null == srcBitmap) {
+            return null;
+        }
+
+        // The gap between the reflection bitmap and original bitmap.
+        final int REFLECTION_GAP = 4;
+
+        int srcWidth = srcBitmap.getWidth();
+        int srcHeight = srcBitmap.getHeight();
+        int reflectionWidth = srcBitmap.getWidth();
+        int reflectionHeight = srcBitmap.getHeight() / 2;
+
+        if (0 == srcWidth || srcHeight == 0) {
+            return null;
+        }
+
+        // The matrix
+        Matrix matrix = new Matrix();
+        matrix.preScale(1, -1);
+
+        try {
+            // The reflection bitmap, width is same with original's, height is
+            // half of original's.
+            Bitmap reflectionBitmap = Bitmap.createBitmap(srcBitmap, 0,
+                    srcHeight / 2, srcWidth, srcHeight / 2, matrix, false);
+
+            if (null == reflectionBitmap) {
+                return null;
+            }
+
+            // Create the bitmap which contains original and reflection bitmap.
+            Bitmap bitmapWithReflection = Bitmap.createBitmap(reflectionWidth,
+                    srcHeight + reflectionHeight + REFLECTION_GAP,
+                    Config.ARGB_8888);
+
+            if (null == bitmapWithReflection) {
+                return null;
+            }
+
+            // Prepare the canvas to draw stuff.
+            Canvas canvas = new Canvas(bitmapWithReflection);
+
+            // Draw the original bitmap.
+            canvas.drawBitmap(srcBitmap, 0, 0, null);
+
+            // Draw the reflection bitmap.
+            canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP,
+                    null);
+
+            // srcBitmap.recycle();
+            reflectionBitmap.recycle();
+
+            Paint paint = new Paint();
+            paint.setAntiAlias(true);
+            LinearGradient shader = new LinearGradient(0, srcHeight, 0,
+                    bitmapWithReflection.getHeight() + REFLECTION_GAP,
+                    0x70FFFFFF, 0x00FFFFFF, TileMode.MIRROR);
+            paint.setShader(shader);
+            paint.setXfermode(new PorterDuffXfermode(
+                    Mode.DST_IN));
+
+            // Draw the linear shader.
+            canvas.drawRect(0, srcHeight, srcWidth,
+                    bitmapWithReflection.getHeight() + REFLECTION_GAP, paint);
+
+            return bitmapWithReflection;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    /**
+     * 计算样本比率
+     *
+     * @param options   Options
+     * @param dstWidth  目标宽度
+     * @param dstHeight 目标高度
+     * @return 根据实际宽高与目标宽高计算出的样本比率
+     */
+    public static int calcSampleSize(Options options, int dstWidth, int dstHeight) {
+        int height = options.outHeight;
+        int width = options.outWidth;
+        int inSampleSize = 1;
+        if (height > dstHeight || width > dstWidth) {
+            int samplesize = Math.round((float) height / (float) dstHeight);
+            inSampleSize = Math.round((float) width / (float) dstWidth);
+            inSampleSize = Math.max(samplesize, inSampleSize);
+        }
+        return inSampleSize;
+    }
+
+    public static Bitmap loadBitmapFromFile(String path, int dstWidth, int dstHeight) {
+        Options options = new Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(path, options);
+        options.inSampleSize = calcSampleSize(options, dstWidth, dstHeight);
+        options.inJustDecodeBounds = false;
+        return BitmapFactory.decodeFile(path, options);
+    }
+
+    public static Bitmap loadBitmapFromFile(String path, int dstWidth, int dstHeight, int minSize) {
+        Options options = new Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(path, options);
+        if (options.outWidth <= minSize && options.outHeight <= minSize) { // 做下特殊处理,小于多少的图片,就整张加载,避免失真
+            options.inSampleSize = 1;
+        } else {
+            options.inSampleSize = calcSampleSize(options, dstWidth, dstHeight);
+        }
+        options.inJustDecodeBounds = false;
+        options.inPurgeable = true;
+        options.inInputShareable = true;
+        return BitmapFactory.decodeFile(path, options);
+    }
+
+    /**
+     * @param path
+     * @param dstWidth
+     */
+    public static int getScaleHeight(String path, int dstWidth) {
+        int dstHeight = 0;
+        try {
+            if (!TextUtils.isEmpty(path) && dstWidth > 0) {
+                Options options = new Options();
+                options.inJustDecodeBounds = true;
+                BitmapFactory.decodeFile(path, options);
+
+                dstHeight = options.outHeight * dstWidth / options.outWidth;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            return dstHeight;
+        }
+    }
+
+    public static Bitmap loadBitmapFromFile(String path) {
+        return BitmapFactory.decodeFile(path);
+    }
+
+    public static Bitmap loadBitmapFromFile(String path, int maxWidth) {
+        Bitmap bm;
+        if (maxWidth > 0) {
+            int dstHeight = BitmapUtils.getScaleHeight(path, maxWidth);
+            bm = loadBitmapFromFile(path, maxWidth, dstHeight);
+        } else {
+            bm = loadBitmapFromFile(path);
+        }
+
+        return bm;
+    }
+
+    public static int calculateInSampleSize(Options options,
+                                            int reqWidth, int reqHeight) {
+        // Raw height and width of image
+        final int height = options.outHeight;
+        final int width = options.outWidth;
+        int inSampleSize = 1;
+        if (height > reqHeight || width > reqWidth) {
+            if (width > height) {
+                inSampleSize = Math.round((float) height / (float) reqHeight);
+            } else {
+                inSampleSize = Math.round((float) width / (float) reqWidth);
+            }
+        }
+        return inSampleSize;
+    }
+
+    public static String saveBitmap(Bitmap bitmap, String path,
+                                    Bitmap.CompressFormat format) {
+        if (path == null || bitmap == null)
+            return null;
+
+        try {
+            BufferedOutputStream bos = new BufferedOutputStream(
+                    new FileOutputStream(path));
+            bitmap.compress(format, 100, bos);
+            bos.flush();
+            bos.close();
+            //bitmap.recycle();
+            return path;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+
+    }
+
+    public static Bitmap scaleBitmap(Bitmap bitmap, float x) {
+
+        int w = bitmap.getWidth();
+        float sx = x / w;
+
+        Matrix matrix = new Matrix();
+        matrix.postScale(sx, sx); // 长和宽放大缩小的比例
+        Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
+                bitmap.getHeight(), matrix, true);
+        return resizeBmp;
+    }
+
+    public static InputStream getRequest(String path) throws Exception {
+        URL url = new URL(path);
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("GET");
+        conn.setConnectTimeout(5000);
+        if (conn.getResponseCode() == 200) {
+            return conn.getInputStream();
+        }
+        return null;
+    }
+
+    public static Drawable getDrawableFromUrl(String url) throws Exception {
+        return Drawable.createFromStream(getRequest(url), null);
+    }
+
+    public static int[] getBitmapSize(Context context, int resourceID) {
+        int[] ret = new int[2];
+        Options opts = new Options();
+        opts.inJustDecodeBounds = true;
+        opts.inDensity = Bitmap.DENSITY_NONE;
+        decodeResource(context.getResources(), resourceID, opts);
+        ret[0] = opts.outWidth;
+        ret[1] = opts.outHeight;
+        return ret;
+    }
+
+    /**
+     * 根据图片文件路径获取图片的大小
+     *
+     * @param path 图片文件路径
+     * @return 图片宽高度
+     */
+    public static int[] getBitmapSize(String path) {
+        try {
+            Options options = new Options();
+            options.inJustDecodeBounds = true;
+            BitmapFactory.decodeFile(path, options);
+            return new int[]{options.outWidth, options.outHeight};
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static Drawable byteToDrawable(byte[] byteArray) {
+        ByteArrayInputStream ins = new ByteArrayInputStream(byteArray);
+        return Drawable.createFromStream(ins, null);
+    }
+
+    public static Bitmap drawableToBitmap(Drawable drawable) {
+
+        Bitmap bitmap = Bitmap
+                .createBitmap(
+                        drawable.getIntrinsicWidth(),
+                        drawable.getIntrinsicHeight(),
+                        drawable.getOpacity() != PixelFormat.OPAQUE ? Config.ARGB_8888
+                                : Config.RGB_565);
+        Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
+                drawable.getIntrinsicHeight());
+        drawable.draw(canvas);
+        return bitmap;
+    }
+
+    public static Bitmap toGrayscale(Bitmap bmpOriginal) {
+        int width, height;
+        height = bmpOriginal.getHeight();
+        width = bmpOriginal.getWidth();
+
+        Bitmap bmpGrayscale = Bitmap.createBitmap(width, height,
+                Config.RGB_565);
+        Canvas c = new Canvas(bmpGrayscale);
+        Paint paint = new Paint();
+        ColorMatrix cm = new ColorMatrix();
+        cm.setSaturation(0);
+        ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
+        paint.setColorFilter(f);
+        c.drawBitmap(bmpOriginal, 0, 0, paint);
+        return bmpGrayscale;
+    }
+
+    public static Bitmap toGrayscale(Bitmap bmpOriginal, int pixels) {
+        return toRoundCorner(toGrayscale(bmpOriginal), pixels);
+    }
+
+    /**
+     * 将指定图片转化成带有指定倒角半径的目标图片
+     *
+     * @param bitmap 需要倒角的位图
+     * @param pixels 倒角半径
+     * @return 倒角后的图片
+     */
+    public static Bitmap toRoundCorner(Bitmap bitmap, int pixels) {
+        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
+        Canvas canvas = new Canvas(output);
+        final Paint paint = new Paint();
+        final int color = 0xff424242;
+        paint.setAntiAlias(true);
+        paint.setColor(color);
+
+        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        final RectF rectF = new RectF(rect);
+
+        canvas.drawARGB(0, 0, 0, 0);
+        canvas.drawRoundRect(rectF, (float) pixels, (float) pixels, paint);
+
+        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+        canvas.drawBitmap(bitmap, rect, rect, paint);
+
+        return output;
+    }
+
+    /**
+     * 保存图片到相册中去
+     *
+     * @param context 上下文
+     * @param bitmap  需要被保存到相册的图片
+     * @param path    图片路径
+     */
+    public static void saveImageToGallery(Context context, Bitmap bitmap, String path) {
+        // 首先保存图片
+        File appDir = new File(path, "photo");
+        if (!appDir.exists()) {
+            appDir.mkdirs();
+        }
+        String fileName = System.currentTimeMillis() + ".jpg";
+        File file = new File(appDir, fileName);
+        try {
+            FileOutputStream fos = new FileOutputStream(file);
+            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
+            fos.flush();
+            fos.close();
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        // 其次把文件插入到系统图库
+        try {
+            MediaStore.Images.Media.insertImage(context.getContentResolver(),
+                    file.getAbsolutePath(), fileName, null);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        }
+        // 最后通知图库更新
+        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
+                Uri.parse("file://" + Uri.fromFile(new File(path + "/photo/" + fileName)))));
+    }
+
+    /******************************************************************/
+
+    /**
+     * 获取缩略图,并保存缩略图到指定目录
+     *
+     * @param srcPath 原图地址
+     * @param desPath 缩略图保存地址
+     * @param width   缩略图宽度
+     */
+    public static Bitmap getImageThumbnailAndSave(String srcPath, String desPath, int width) {
+        try {
+            Bitmap bitmap = getImageThumbnail(srcPath, width);
+            int angle = getExifOrientation(srcPath);
+            if (angle > 0) {
+                Matrix matrix = new Matrix();
+                matrix.postRotate(angle);
+                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+            }
+            FileOutputStream fos = new FileOutputStream(desPath);
+            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
+            fos.close();
+            return bitmap;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取缩略图
+     *
+     * @param srcPath 原图片地址
+     * @param width   最大图片宽度
+     */
+    public static Bitmap getImageThumbnail(String srcPath, int width) {
+        Options options = new Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(srcPath, options);
+        int h = options.outHeight;
+        int w = options.outWidth;
+        int height = h * width / w;
+        return getImageThumbnail(srcPath, width, height);
+    }
+
+    /**
+     * 获取缩略图
+     *
+     * @param imagePath 图片地址
+     * @param width     最大图片宽度
+     * @param height    最大图片高度
+     */
+    public static Bitmap getImageThumbnail(String imagePath, int width, int height) {
+        Options options = new Options();
+        options.inPreferredConfig = Config.ARGB_8888;
+        options.inPurgeable = true;
+        options.inInputShareable = true;
+        options.inJustDecodeBounds = true;
+        //这个值是压缩的倍数(2的整数倍),数值越小,压缩率越小,图片越清晰
+        options.inSampleSize = 2;
+        // 获取这个图片的宽和高,注意此处的bitmap为null
+        BitmapFactory.decodeFile(imagePath, options);
+        // 计算缩放比
+        int h = options.outHeight;
+        int w = options.outWidth;
+        int beWidth = w / width;
+        int beHeight = h / height;
+        int be;
+        if (beWidth < beHeight) {
+            be = beWidth;
+        } else {
+            be = beHeight;
+        }
+        if (be <= 0) {
+            be = 1;
+        }
+        options.inSampleSize = be;
+        // 重新读入图片,读取缩放后的bitmap,注意这次要把options.inJustDecodeBounds 设为 false
+        options.inJustDecodeBounds = false;
+        Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
+        if (be == 1) {
+            return bitmap;
+        }
+        // 利用ThumbnailUtils来创建缩略图,这里要指定要缩放哪个Bitmap对象
+        bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
+        return bitmap;
+    }
+
+    /**
+     * 得到 图像信息的旋转 的角度
+     * Exif : 图像信息
+     *
+     * @param srcPath 图片地址
+     * @return 图片旋转 的角度
+     */
+    private static int getExifOrientation(String srcPath) {
+        int degree = 0;
+        ExifInterface exif = null;
+        try {
+            exif = new ExifInterface(srcPath);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        if (exif != null) {
+            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
+            if (orientation != -1) {
+                switch (orientation) {
+                    case ExifInterface.ORIENTATION_ROTATE_90:
+                        degree = 90;
+                        break;
+                    case ExifInterface.ORIENTATION_ROTATE_180:
+                        degree = 180;
+                        break;
+                    case ExifInterface.ORIENTATION_ROTATE_270:
+                        degree = 270;
+                        break;
+                }
+            }
+        }
+        return degree;
+    }
+
+    /**
+     * 转换图片成圆形
+     *
+     * @param bitmap 传入Bitmap对象
+     * @return 转换成圆形后的图片
+     */
+    public static Bitmap toRoundBitmap(Bitmap bitmap) {
+        int width = bitmap.getWidth();
+        int height = bitmap.getHeight();
+        float roundPx;
+        float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;
+        if (width <= height) {
+            roundPx = width / 2;
+            left = 0;
+            top = 0;
+            right = width;
+            bottom = width;
+            height = width;
+            dst_left = 0;
+            dst_top = 0;
+            dst_right = width;
+            dst_bottom = width;
+        } else {
+            roundPx = height / 2;
+            float clip = (width - height) / 2;
+            left = clip;
+            right = width - clip;
+            top = 0;
+            bottom = height;
+            width = height;
+            dst_left = 0;
+            dst_top = 0;
+            dst_right = height;
+            dst_bottom = height;
+        }
+
+        Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888);
+        Canvas canvas = new Canvas(output);
+
+        final int color = 0xff424242;
+        final Paint paint = new Paint();
+        final Rect src = new Rect((int) left, (int) top, (int) right, (int) bottom);
+        final Rect dst = new Rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom);
+        final RectF rectF = new RectF(dst);
+
+        paint.setAntiAlias(true);// 设置画笔无锯齿
+
+        canvas.drawARGB(0, 0, 0, 0); // 填充整个Canvas
+        paint.setColor(color);
+
+        // 以下有两种方法画圆,drawRounRect和drawCircle
+        // canvas.drawRoundRect(rectF, roundPx, roundPx, paint);//
+        // 画圆角矩形,第一个参数为图形显示区域,第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。
+        canvas.drawCircle(roundPx, roundPx, roundPx, paint);
+
+        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));// 设置两张图片相交时的模式,参考http://trylovecatch.iteye.com/blog/1189452
+        canvas.drawBitmap(bitmap, src, dst, paint); // 以Mode.SRC_IN模式合并bitmap和已经draw了的Circle
+
+        return output;
+    }
+
+    /**
+     * 雾化效果
+     *
+     * @param sentBitmap       Bitmap
+     * @param canReuseInBitmap boolean
+     * @return Bitmap
+     */
+    public static Bitmap doBlur(Bitmap sentBitmap, boolean canReuseInBitmap) {
+        // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
+
+        Bitmap bitmap;
+        int radius = 5;
+        if (canReuseInBitmap) {
+            bitmap = sentBitmap;
+        } else {
+            bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
+        }
+
+        int w = bitmap.getWidth();
+        int h = bitmap.getHeight();
+
+        int[] pix = new int[w * h];
+        bitmap.getPixels(pix, 0, w, 0, 0, w, h);
+
+        int wm = w - 1;
+        int hm = h - 1;
+        int wh = w * h;
+        int div = radius + radius + 1;
+
+        int r[] = new int[wh];
+        int g[] = new int[wh];
+        int b[] = new int[wh];
+        int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
+        int vmin[] = new int[Math.max(w, h)];
+
+        int divsum = (div + 1) >> 1;
+        divsum *= divsum;
+        int dv[] = new int[256 * divsum];
+        for (i = 0; i < 256 * divsum; i++) {
+            dv[i] = (i / divsum);
+        }
+
+        yw = yi = 0;
+
+        int[][] stack = new int[div][3];
+        int stackpointer;
+        int stackstart;
+        int[] sir;
+        int rbs;
+        int r1 = radius + 1;
+        int routsum, goutsum, boutsum;
+        int rinsum, ginsum, binsum;
+
+        for (y = 0; y < h; y++) {
+            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
+            for (i = -radius; i <= radius; i++) {
+                p = pix[yi + Math.min(wm, Math.max(i, 0))];
+                sir = stack[i + radius];
+                sir[0] = (p & 0xff0000) >> 16;
+                sir[1] = (p & 0x00ff00) >> 8;
+                sir[2] = (p & 0x0000ff);
+                rbs = r1 - Math.abs(i);
+                rsum += sir[0] * rbs;
+                gsum += sir[1] * rbs;
+                bsum += sir[2] * rbs;
+                if (i > 0) {
+                    rinsum += sir[0];
+                    ginsum += sir[1];
+                    binsum += sir[2];
+                } else {
+                    routsum += sir[0];
+                    goutsum += sir[1];
+                    boutsum += sir[2];
+                }
+            }
+            stackpointer = radius;
+
+            for (x = 0; x < w; x++) {
+
+                r[yi] = dv[rsum];
+                g[yi] = dv[gsum];
+                b[yi] = dv[bsum];
+
+                rsum -= routsum;
+                gsum -= goutsum;
+                bsum -= boutsum;
+
+                stackstart = stackpointer - radius + div;
+                sir = stack[stackstart % div];
+
+                routsum -= sir[0];
+                goutsum -= sir[1];
+                boutsum -= sir[2];
+
+                if (y == 0) {
+                    vmin[x] = Math.min(x + radius + 1, wm);
+                }
+                p = pix[yw + vmin[x]];
+
+                sir[0] = (p & 0xff0000) >> 16;
+                sir[1] = (p & 0x00ff00) >> 8;
+                sir[2] = (p & 0x0000ff);
+
+                rinsum += sir[0];
+                ginsum += sir[1];
+                binsum += sir[2];
+
+                rsum += rinsum;
+                gsum += ginsum;
+                bsum += binsum;
+
+                stackpointer = (stackpointer + 1) % div;
+                sir = stack[(stackpointer) % div];
+
+                routsum += sir[0];
+                goutsum += sir[1];
+                boutsum += sir[2];
+
+                rinsum -= sir[0];
+                ginsum -= sir[1];
+                binsum -= sir[2];
+
+                yi++;
+            }
+            yw += w;
+        }
+        for (x = 0; x < w; x++) {
+            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
+            yp = -radius * w;
+            for (i = -radius; i <= radius; i++) {
+                yi = Math.max(0, yp) + x;
+
+                sir = stack[i + radius];
+
+                sir[0] = r[yi];
+                sir[1] = g[yi];
+                sir[2] = b[yi];
+
+                rbs = r1 - Math.abs(i);
+
+                rsum += r[yi] * rbs;
+                gsum += g[yi] * rbs;
+                bsum += b[yi] * rbs;
+
+                if (i > 0) {
+                    rinsum += sir[0];
+                    ginsum += sir[1];
+                    binsum += sir[2];
+                } else {
+                    routsum += sir[0];
+                    goutsum += sir[1];
+                    boutsum += sir[2];
+                }
+
+                if (i < hm) {
+                    yp += w;
+                }
+            }
+            yi = x;
+            stackpointer = radius;
+            for (y = 0; y < h; y++) {
+                // Preserve alpha channel: ( 0xff000000 & pix[yi] )
+                pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
+
+                rsum -= routsum;
+                gsum -= goutsum;
+                bsum -= boutsum;
+
+                stackstart = stackpointer - radius + div;
+                sir = stack[stackstart % div];
+
+                routsum -= sir[0];
+                goutsum -= sir[1];
+                boutsum -= sir[2];
+
+                if (x == 0) {
+                    vmin[y] = Math.min(y + r1, hm) * w;
+                }
+                p = x + vmin[y];
+
+                sir[0] = r[p];
+                sir[1] = g[p];
+                sir[2] = b[p];
+
+                rinsum += sir[0];
+                ginsum += sir[1];
+                binsum += sir[2];
+
+                rsum += rinsum;
+                gsum += ginsum;
+                bsum += binsum;
+
+                stackpointer = (stackpointer + 1) % div;
+                sir = stack[stackpointer];
+
+                routsum += sir[0];
+                goutsum += sir[1];
+                boutsum += sir[2];
+
+                rinsum -= sir[0];
+                ginsum -= sir[1];
+                binsum -= sir[2];
+
+                yi += w;
+            }
+        }
+
+        bitmap.setPixels(pix, 0, w, 0, 0, w, h);
+
+        return (bitmap);
+    }
+
+    /**
+     * 通过图片文件路径验证图片大小
+     *
+     * @param path 需要验证的图片的文件路径
+     * @return true为尺寸小于1280*720
+     */
+    public static boolean verifyPictureSize(String path) {
+        Options opts = new Options();
+        opts.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(path, opts);
+        return opts.outHeight <= 1280 && opts.outWidth <= 720;
+    }
+
+    /**
+     * 通过图片文件路径列表获取图片总大小
+     *
+     * @param pathList 图片文件列表
+     * @return 大小
+     */
+    public static String getPictureSize(List<String> pathList) {
+        long totalSize = 0;
+        for (String path : pathList) {
+            File file = new File(path);
+            if (file.exists() && file.isFile())
+                totalSize += file.length();
+        }
+        NumberFormat format = NumberFormat.getNumberInstance();
+        //保留小数点后两位
+        format.setMaximumFractionDigits(2);
+        double size = totalSize / 1048576.0;
+        return format.format(size) + "M";
+    }
+
+
+    /**
+     * 从文件中获取图片,指定宽高
+     *
+     * @param path   图片文件路径
+     * @param width  指定宽度
+     * @param height 指定高度
+     * @return 目标图片
+     */
+    public static Bitmap getBitmapFromFile(String path, int width, int height) {
+        Options opts = null;
+        if (path != null) {
+            if (width > 0 && height > 0) {
+                opts = new Options();
+                opts.inJustDecodeBounds = true;
+                BitmapFactory.decodeFile(path, opts);
+                int minSideLength = Math.min(width, height);
+                opts.inSampleSize = computeSampleSize(opts, minSideLength, width * height);
+                opts.inJustDecodeBounds = false;
+            }
+            return BitmapFactory.decodeFile(path, opts);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * 计算样本比率大小
+     *
+     * @param options        Options
+     * @param minSideLength  最小边长度
+     * @param maxNumOfPixels 总像素
+     * @return 样本比率
+     */
+    public static int computeSampleSize(Options options, int minSideLength, int maxNumOfPixels) {
+        int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
+        int roundedSize;
+        if (initialSize <= 8) {
+            roundedSize = 1;
+            while (roundedSize < initialSize) {
+                roundedSize <<= 1;
+            }
+        } else {
+            roundedSize = (initialSize + 7) / 8 * 8;
+        }
+        return roundedSize;
+    }
+
+    /**
+     * 计算初始样本比率
+     *
+     * @param options        Options
+     * @param minSideLength  最小边长度
+     * @param maxNumOfPixels 总像素
+     * @return 初始样本比率
+     */
+    private static int computeInitialSampleSize(Options options, int minSideLength, int maxNumOfPixels) {
+        double w = options.outWidth;
+        double h = options.outHeight;
+        int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
+        int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
+        if (upperBound < lowerBound) {
+            // return the larger one when there is no overlapping zone.
+            return lowerBound;
+        }
+        if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
+            return 1;
+        } else if (minSideLength == -1) {
+            return lowerBound;
+        } else {
+            return upperBound;
+        }
+    }
+
+    /**
+     * 根据bitmap保存图片到本地
+     *
+     * @param bitmap   Bitmap
+     * @param userName 发送给的用户名
+     * @return 图片路径
+     */
+    public static String saveBitmapToLocal(Bitmap bitmap, String userName) {
+        if (null == bitmap) {
+            return null;
+        }
+        String filePath;
+        FileOutputStream fileOutput = null;
+        File imgFile;
+        try {
+            imgFile = new File("/sdcard/", userName + ".png");
+            fileOutput = new FileOutputStream(imgFile);
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutput);
+            fileOutput.flush();
+            filePath = imgFile.getAbsolutePath();
+        } catch (IOException e) {
+            e.printStackTrace();
+            filePath = null;
+        } finally {
+            if (null != fileOutput) {
+                try {
+                    fileOutput.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return filePath;
+    }
+
+    /**
+     * 图片bitmap与uri互相转换
+     *
+     * @param context Context
+     * @param bitmap  Bitmap
+     * @return Uri
+     */
+    public static Uri bitmapToUri(Context context, Bitmap bitmap) {
+        return Uri.parse(MediaStore.Images.Media.insertImage(context.getContentResolver(), bitmap, null, null));
+    }
+
+    /**
+     * 图片bitmap与uri互相转换
+     *
+     * @param context Context
+     * @param uri     Uri
+     * @return Bitmap
+     */
+    public static Bitmap uriToBitmap(Context context, Uri uri) {
+        try {
+            return MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 旋转图片,使图片保持正确的方向。
+     *
+     * @param bitmap  原始图片
+     * @param degrees 原始图片的角度
+     * @return Bitmap 旋转后的图片
+     */
+    public static Bitmap rotateBitmap(Bitmap bitmap, int degrees) {
+        if (degrees == 0 || null == bitmap) {
+            return bitmap;
+        }
+        Matrix matrix = new Matrix();
+        matrix.setRotate(degrees, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
+
+        Bitmap bitmapRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+        bitmap.recycle();
+        return bitmapRotated;
+    }
+
+    public static Bitmap getBitmap(Context mContext, int resId) {
+        return BitmapFactory.decodeResource(mContext.getResources(), resId).copy(Config.RGB_565, true);
+    }
+
+    public static Bitmap cropBitmap(Bitmap bitmap, RectF rect) {
+        Bitmap result = Bitmap.createBitmap(bitmap, (int) rect.left, (int) rect.top, (int) (rect.right - rect.left), (int) (rect.bottom - rect.top));
+        return result;
+    }
+
+    public static Bitmap decodeImage(String path) {
+        Bitmap res;
+        try {
+            ExifInterface exif = new ExifInterface(path);
+            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
+
+            Options op = new Options();
+            op.inSampleSize = 1;
+            op.inJustDecodeBounds = false;
+            //op.inMutable = true;
+            res = BitmapFactory.decodeFile(path, op);
+            //rotate and scale.
+            Matrix matrix = new Matrix();
+
+            if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
+                matrix.postRotate(90);
+            } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
+                matrix.postRotate(180);
+            } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
+                matrix.postRotate(270);
+            }
+
+            Bitmap temp = Bitmap.createBitmap(res, 0, 0, res.getWidth(), res.getHeight(), matrix, true);
+            Log.d("com.arcsoft", "check target Image:" + temp.getWidth() + "X" + temp.getHeight());
+
+            if (!temp.equals(res)) {
+                res.recycle();
+            }
+            return temp;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+  /*  *//**
+     * 中控提供的将输出流转化为图片
+     *
+     * @param bgrbuf
+     * @return
+     *//*
+    public static Bitmap Bgr2Bitmap(byte[] bgrbuf) {
+        int width = WLTService.imgWidth;
+        int height = WLTService.imgHeight;
+        Bitmap bmp = Bitmap.createBitmap(width, height, Config.RGB_565);
+        int row = 0, col = width - 1;
+        for (int i = bgrbuf.length - 1; i >= 3; i -= 3) {
+            int color = bgrbuf[i] & 0xFF;
+            color += (bgrbuf[i - 1] << 8) & 0xFF00;
+            color += ((bgrbuf[i - 2]) << 16) & 0xFF0000;
+            bmp.setPixel(col--, row, color);
+            if (col < 0) {
+                col = width - 1;
+                row++;
+            }
+        }
+        return bmp;
+    }*/
+
+    /**
+     * 保存图片为JPEG
+     *
+     * @param bitmap
+     * @param path
+     */
+    public static void saveJPGE_After(Context context, Bitmap bitmap, String path, int quality) {
+        File file = new File(path);
+        makeDir(file);
+        try {
+            FileOutputStream out = new FileOutputStream(file);
+            if (bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out)) {
+                out.flush();
+                out.close();
+            }
+            if(context!=null){
+                MediaScannerConnection.scanFile(context, new String[]{path}, null, null);
+            }
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void makeDir(File file) {
+        File tempPath = new File(file.getParent());
+        if (!tempPath.exists()) {
+            tempPath.mkdirs();
+        }
+    }
+
+
+    /**
+     * @方法描述 将RGB字节数组转换成Bitmap,
+     */
+    public static Bitmap rgb2Bitmap(byte[] data, int width, int height) {
+        int[] colors = convertByteToColor(data);    //取RGB值转换为int数组
+        if (colors == null) {
+            return null;
+        }
+
+        Bitmap bmp = Bitmap.createBitmap(colors, 0, width, width, height,
+                Config.ARGB_8888);
+        return bmp;
+    }
+
+
+    // 将一个byte数转成int
+    // 实现这个函数的目的是为了将byte数当成无符号的变量去转化成int
+    public static int convertByteToInt(byte data) {
+
+        int heightBit = (int) ((data >> 4) & 0x0F);
+        int lowBit = (int) (0x0F & data);
+        return heightBit * 16 + lowBit;
+    }
+
+
+    // 将纯RGB数据数组转化成int像素数组
+    public static int[] convertByteToColor(byte[] data) {
+        int size = data.length;
+        if (size == 0) {
+            return null;
+        }
+
+        int arg = 0;
+        if (size % 3 != 0) {
+            arg = 1;
+        }
+
+        // 一般RGB字节数组的长度应该是3的倍数,
+        // 不排除有特殊情况,多余的RGB数据用黑色0XFF000000填充
+        int[] color = new int[size / 3 + arg];
+        int red, green, blue;
+        int colorLen = color.length;
+        if (arg == 0) {
+            for (int i = 0; i < colorLen; ++i) {
+                red = convertByteToInt(data[i * 3]);
+                green = convertByteToInt(data[i * 3 + 1]);
+                blue = convertByteToInt(data[i * 3 + 2]);
+
+                // 获取RGB分量值通过按位或生成int的像素值
+                color[i] = (red << 16) | (green << 8) | blue | 0xFF000000;
+            }
+        } else {
+            for (int i = 0; i < colorLen - 1; ++i) {
+                red = convertByteToInt(data[i * 3]);
+                green = convertByteToInt(data[i * 3 + 1]);
+                blue = convertByteToInt(data[i * 3 + 2]);
+                color[i] = (red << 16) | (green << 8) | blue | 0xFF000000;
+            }
+
+            color[colorLen - 1] = 0xFF000000;
+        }
+
+        return color;
+    }
+
+    /**
+     * 照片转byte二进制
+     * @param imagepath 需要转byte的照片路径
+     * @return 已经转成的byte
+     * @throws Exception
+     */
+    public static byte[] readStream(String imagepath) throws Exception {
+        FileInputStream fs = new FileInputStream(imagepath);
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int len = 0;
+        while (-1 != (len = fs.read(buffer))) {
+            outStream.write(buffer, 0, len);
+        }
+        outStream.close();
+        fs.close();
+        return outStream.toByteArray();
+    }
+
+    public static final Bitmap getBitmap(ContentResolver cr, Uri url)
+            throws FileNotFoundException, IOException {
+        InputStream input = cr.openInputStream(url);
+        Bitmap bitmap = BitmapFactory.decodeStream(input);
+        input.close();
+        return bitmap;
+    }
+    /**
+     * 将base64字符串转为bitmap
+     * @param base64String
+     * @return bitmap
+     */
+    public static Bitmap base64ToBitmap(String base64String) {
+
+        byte[] bytes = Base64.decode(base64String, Base64.NO_WRAP);
+        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+        return bitmap;
+
+    }
+//    /**
+//     * Bitmap转化为ARGB数据,再转化为NV21数据
+//     *
+//     * @param src    传入ARGB_8888的Bitmap
+//     * @param width  NV21图像的宽度
+//     * @param height NV21图像的高度
+//     * @return nv21数据
+//     */
+
+
+    public static String transToFileFromNv21(String fileName, byte[] data, int width, int height, Rect rect) {
+        //保存一张照片
+        String dirPath = Constant.RecognitionDir;    //系统路径
+        File mkDir = new File(dirPath);
+        if (!mkDir.exists()) {
+            mkDir.mkdirs();   //目录不存在,则创建
+        }
+        if (rect == null) {
+            rect = new Rect(0, 0, width, height);
+        }
+
+        final YuvImage yuv = new YuvImage(data, ImageFormat.NV21, width, height, null);
+        ExtByteArrayOutputStream ops = new ExtByteArrayOutputStream();
+        try {
+            yuv.compressToJpeg(rect, 100, ops);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+//        saveImageNV21(imageNV21, mWidth, mHeight, rect, 80, TimeUtil.longToString(System.currentTimeMillis(), "yyyyMMddHHmmss") + "识别2" + ".jpg");
+
+        Bitmap bmp = BitmapFactory.decodeByteArray(ops.getByteArray(), 0, ops.getByteArray().length);
+        if (bmp == null) {
+            return null;
+        }
+        try {
+            ops.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+
+        Bitmap bitmap = null;
+        if (Constant.isFrontCamera) {
+            //竖屏状态下,bmp会在服务端显示左转90度
+            bitmap = BitmapUtils.rotateBitmap1(bmp, Constant.rotate);
+        } else {
+            bitmap = BitmapUtils.rotateBitmap1(bmp, Constant.rotate);
+        }
+
+        bitmap = BitmapUtils.scaleBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight());
+        File file = new File(dirPath + fileName + ".jpg");
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream(file);
+            if (bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out)) {
+                bitmap.recycle();
+            }
+            out.flush();
+            out.close();
+            bmp.recycle();
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return "file://" + dirPath + fileName + ".jpg";
+    }
+
+}

+ 74 - 0
app/src/main/java/com/sunwin/visitorapp/utils/Constant.java

@@ -0,0 +1,74 @@
+package com.sunwin.visitorapp.utils;
+
+
+public class Constant {
+
+    final public static int rotate = SharePrefenceUtils.getInt("cameraRotate", 0);
+    final public static boolean isFrontCamera = SharePrefenceUtils.getBoolean("isFrontCamera", false);
+    final public static boolean isSufaRotate = SharePrefenceUtils.getBoolean("sufaRotate", false);
+    public static int  COMPARSION_VALUE = SharePrefenceUtils.getInt("comparsion_value", Constant.NumerValue.defaultComparsionValue);
+    public static int COMPARSION_FREQUENCY = SharePrefenceUtils.getInt("comparsion_frequency_value", Constant.NumerValue.defaultComparsionFrequency);
+    public int AVOID_SAME_PEOPLE_VALUE = SharePrefenceUtils.getInt("avoid_same_people_value", Constant.NumerValue.defaultAvoidSamePeopleValue);
+    public int MOUDLE_UPDATE_VALUE = SharePrefenceUtils.getInt("mould_update_value", Constant.NumerValue.defaultMoudleUpdateValue);
+    public int FACE_QULITY = SharePrefenceUtils.getInt("face_quality", Constant.NumerValue.defaultFaceQuality);
+    public static int FACE_ID_COMPARE_VALUE = SharePrefenceUtils.getInt("faceIdCompareValue", Constant.NumerValue.defaultIdCompareValue);
+
+    public static int FACE_MIN_FACE_VALUE = SharePrefenceUtils.getInt("minfacevalue", Constant.NumerValue.defaultMinFaceValue);
+
+    public static int isUse = SharePrefenceUtils.getInt("isUse", 1);
+
+    public static String BASE_HOST = "http://119.39.226.171:20040/";//正式服务器
+    public static final String SWBINDIR = "/sdcard/models";
+    public static final String SWRECODE = "/sdcard/swRecode";
+
+    public static final String RecognitionDir = "/sdcard/faceRecognition/"; //记录图片存储位置
+
+    public static volatile boolean isReConnectUsb = false;//是否重新连接
+
+    public static String getSERVER() {
+        String serverUrl = SharePrefenceUtils.getString(ISharePrefence.BASEURL, BASE_HOST);
+        return serverUrl;
+    }
+
+
+    public interface ISharePrefence {
+
+        String BASEURL = "baseurl";
+        String LOGINTAG = "logintag";
+        String ISINIT = "isInit";
+    }
+
+    public interface ITable {//数据库表相关字段
+        String T_ACCOUNT = "t_account";
+    }
+
+    public interface IIntentValue {
+    }
+
+    public interface NumerValue {
+
+      int VID = 1024;    //IDR VID
+      int PID = 50010;     //IDR PID
+
+
+        int FRABS_TYPE = 1;
+        boolean isUseIdface = true; //是否使用日月sdk
+        long TIME_CLOSE_DIALOG = 2000; //弹框默认显示时间为2s。
+
+        int cameraNums = 1;
+
+        /**
+         * 比对设置参数
+         */
+        int defaultComparsionValue = 55;
+        int defaultMoudleUpdateValue = 100;
+        int defaultAvoidSamePeopleValue = 80;
+        int defaultComparsionFrequency = 3;
+        int defaultFaceQuality = 60;
+        int defaultIdCompareValue = 55;
+       int defaultMinFaceValue = 100;
+
+
+      int AppType = 3;//0pad 1手持2海清3酒店人证
+    }
+}

+ 0 - 30
app/src/main/java/com/sunwin/visitorapp/utils/Constants.java

@@ -1,30 +0,0 @@
-package com.sunwin.visitorapp.utils;
-
-
-public class Constants {
-
-
-    public static String BASE_HOST = "http://119.39.226.171:20040/";//正式服务器
-
-    public static String getSERVER() {
-        String serverUrl = SharePrefenceUtils.getString(ISharePrefence.BASEURL, BASE_HOST);
-        return serverUrl;
-    }
-
-
-    public interface ISharePrefence {
-
-        String BASEURL = "baseurl";
-        String LOGINTAG = "logintag";
-    }
-
-    public interface ITable {//数据库表相关字段
-        String T_ACCOUNT = "t_account";
-    }
-
-    public interface IIntentValue {
-    }
-
-    public interface NumerValue {
-    }
-}

File diff suppressed because it is too large
+ 236 - 0
app/src/main/java/com/sunwin/visitorapp/utils/ConvertUtil.java


+ 38 - 0
app/src/main/java/com/sunwin/visitorapp/utils/FileUtil.java

@@ -0,0 +1,38 @@
+package com.sunwin.visitorapp.utils;
+
+import android.content.Context;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+
+public class FileUtil {
+    public static void copyFilesFromAssets(Context context, String assetsPath, String savePath){
+        try {
+            String fileNames[] = context.getAssets().list(assetsPath);// 获取assets目录下的所有文件及目录名
+            if (fileNames.length > 0) {// 如果是目录
+                File file = new File(savePath);
+                file.mkdirs();// 如果文件夹不存在,则递归
+                for (String fileName : fileNames) {
+                    copyFilesFromAssets(context, assetsPath + "/" + fileName,
+                            savePath + "/" + fileName);
+                }
+            } else {// 如果是文件
+                InputStream is = context.getAssets().open(assetsPath);
+                FileOutputStream fos = new FileOutputStream(new File(savePath));
+                byte[] buffer = new byte[1024];
+                int byteCount = 0;
+                while ((byteCount = is.read(buffer)) != -1) {// 循环从输入流读取
+                    // buffer字节
+                    fos.write(buffer, 0, byteCount);// 将读取的输入流写入到输出流
+                }
+                fos.flush();// 刷新缓冲区
+                is.close();
+                fos.close();
+            }
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+}

+ 43 - 0
app/src/main/java/com/sunwin/visitorapp/utils/ImageInf.java

@@ -0,0 +1,43 @@
+package com.sunwin.visitorapp.utils;
+
+
+
+public class ImageInf {
+    public byte[] faceImg;
+    public int width;
+    public int height;
+    public int imgFormat;// 0-SW_IMAGE_GRAY,1-SW_IMAGE_RGB,2-SW_IMAGE_BGR,3-SW_IMAGE_YUV420P,4-SW_IMAGE_YV12,5-SW_IMAGE_NV12,6-SW_IMAGE_NV21,
+    public int imgAngle;//0-没有旋转,90-逆时针旋转90度,180-逆时针旋转180度,270-逆时针旋转270度
+
+    public byte[] getFaceImg() {
+        return faceImg;
+    }
+
+    public void setFaceImg(byte[] faceImg) {
+        this.faceImg = faceImg;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    public int getImgFormat() {
+        return imgFormat;
+    }
+
+    public void setImgFormat(int imgFormat) {
+        this.imgFormat = imgFormat;
+    }
+}

+ 7 - 1
app/src/main/java/com/sunwin/visitorapp/utils/LogUtil.java

@@ -5,7 +5,7 @@ import android.util.Log;
 
 public class LogUtil {
     public static final String TAG = "LogUtil";
-    public static boolean isDebug = false;
+    public static boolean isDebug = true;
 
     public static void debugLog(String msg) {
         if (isDebug) {
@@ -26,6 +26,12 @@ public class LogUtil {
         }
     }
 
+    public static void v(String tag, String msg) {
+        if (isDebug) {
+            Log.v(tag, msg);
+        }
+    }
+
     public static void e(String tag, String msg) {
         if (isDebug) {
             Log.e(tag, msg);

+ 41 - 0
app/src/main/java/com/sunwin/visitorapp/utils/NV21ToBitmap.java

@@ -0,0 +1,41 @@
+package com.sunwin.visitorapp.utils;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicYuvToRGB;
+import android.renderscript.Type;
+
+public class NV21ToBitmap {
+    private RenderScript rs;
+    private ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic;
+    private Type.Builder yuvType, rgbaType;
+    private Allocation in, out;
+
+    public NV21ToBitmap(Context context) {
+        rs = RenderScript.create(context);
+        yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
+    }
+
+    public Bitmap nv21ToBitmap(byte[] nv21, int width, int height) {
+        if (yuvType == null) {
+            yuvType = new Type.Builder(rs, Element.U8(rs)).setX(nv21.length);
+            in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
+
+            rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
+            out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
+        }
+
+        in.copyFrom(nv21);
+
+        yuvToRgbIntrinsic.setInput(in);
+        yuvToRgbIntrinsic.forEach(out);
+
+        Bitmap bmpout = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        out.copyTo(bmpout);
+
+        return bmpout;
+    }
+}

+ 112 - 0
app/src/main/java/com/sunwin/visitorapp/utils/SystemTTS.java

@@ -0,0 +1,112 @@
+package com.sunwin.visitorapp.utils;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
+import android.speech.tts.UtteranceProgressListener;
+
+import java.util.Locale;
+
+/**
+ * 系统播报类,部分手机不支持中文播报
+ */
+@SuppressLint("NewApi")
+public class SystemTTS extends UtteranceProgressListener implements OnUtteranceCompletedListener {
+    private Context mContext;
+    private static SystemTTS singleton;
+    private TextToSpeech textToSpeech; // 系统语音播报类
+    private boolean isSuccess = true;
+    private static volatile boolean isSpeeching = false;
+
+    public static SystemTTS getInstance(Context context) {
+        if (singleton == null) {
+            synchronized (SystemTTS.class) {
+                if (singleton == null) {
+                    singleton = new SystemTTS(context);
+                }
+            }
+        }
+        return singleton;
+    }
+
+    private SystemTTS(Context context) {
+        this.mContext = context.getApplicationContext();
+        textToSpeech = new TextToSpeech(mContext, new TextToSpeech.OnInitListener() {
+            @Override
+            public void onInit(int i) {
+                //系统语音初始化成功
+                if (i == TextToSpeech.SUCCESS) {
+                    int result = textToSpeech.setLanguage(Locale.CHINA);
+                    textToSpeech.setPitch(1.0f);// 设置音调,值越大声音越尖(女生),值越小则变成男声,1.0是常规
+                    textToSpeech.setSpeechRate(1.0f);
+                    textToSpeech.setOnUtteranceProgressListener(SystemTTS.this);
+                    textToSpeech.setOnUtteranceCompletedListener(SystemTTS.this);
+                    if (result == TextToSpeech.LANG_MISSING_DATA
+                            || result == TextToSpeech.LANG_NOT_SUPPORTED) {
+                        //系统不支持中文播报
+                        isSuccess = false;
+                    }
+                }
+
+            }
+        });
+    }
+
+
+    public void destroy() {
+        stopSpeak();
+        if (textToSpeech != null) {
+            textToSpeech.shutdown();
+        }
+        singleton = null;
+    }
+
+    public void init() {
+    }
+
+    public void playText(String playText) {
+        if (!isSuccess) {
+            return;
+        }
+        if(isSpeeching){
+            return;
+        }
+        if (textToSpeech != null) {
+            isSpeeching = true;
+            textToSpeech.speak(playText,
+                    TextToSpeech.QUEUE_ADD, null, null);
+            isSpeeching = false;
+        }
+    }
+
+    public void stopSpeak() {
+        if (textToSpeech != null) {
+            textToSpeech.stop();
+        }
+    }
+
+    public boolean isPlaying() {
+        return textToSpeech.isSpeaking();
+    }
+
+
+    //播报完成回调
+    @Override
+    public void onUtteranceCompleted(String utteranceId) {
+    }
+
+    @Override
+    public void onStart(String utteranceId) {
+
+    }
+
+    @Override
+    public void onDone(String utteranceId) {
+    }
+
+    @Override
+    public void onError(String utteranceId) {
+
+    }
+}

+ 6 - 0
app/src/main/java/com/sunwin/visitorapp/utils/ToastUtils.java

@@ -47,4 +47,10 @@ public class ToastUtils {
         toast.show();
     }
 
+    public static void showToast(int resId) {
+        Toast toast = Toast.makeText(BaseApplication.getInstance(), resId, Toast.LENGTH_SHORT);
+        toast.setGravity(Gravity.CENTER, 0, 0);
+        toast.show();
+    }
+
 }

+ 46 - 0
app/src/main/java/com/sunwin/visitorapp/utils/UniversalImageLoaderConfiguration.java

@@ -0,0 +1,46 @@
+package com.sunwin.visitorapp.utils;
+
+import android.content.Context;
+
+import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator;
+import com.nostra13.universalimageloader.core.DisplayImageOptions;
+import com.nostra13.universalimageloader.core.ImageLoader;
+import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
+
+public class UniversalImageLoaderConfiguration {
+
+    /**
+     * 配置UniversalImageLoader
+     * @param context 上下文
+     * @param defaultImage 默认图片(用于*图片uri为空*、*加载失败*、*正在加载中的三种情形*)
+     */
+    public static void configure(Context context, int defaultImage) {
+        configure(context, defaultImage, defaultImage, defaultImage);
+    }
+
+    /**
+     * 配置UniversalImageLoader
+     * @param context 上下文
+     * @param emptyUriImage 默认图片(*图片uri为空*)
+     * @param failImage 默认图片(*加载失败*)
+     * @param loadingImage 默认图片(*正在加载中的三种情形*)
+     */
+    public static void configure(Context context, int emptyUriImage, int failImage, int loadingImage) {
+        DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder() //
+                .showImageForEmptyUri(emptyUriImage) //
+                .showImageOnFail(failImage) //
+                .showImageOnLoading(loadingImage)
+                .cacheInMemory(true) //
+                .cacheOnDisk(true) //
+                .build();//
+        ImageLoaderConfiguration config = new ImageLoaderConfiguration//
+                .Builder(context)//
+                .defaultDisplayImageOptions(defaultOptions)//
+                //  .discCacheSize(50 * 1024 * 1024)//
+                //  .discCacheFileCount(100)// 缓存一百张图片
+                .writeDebugLogs()//
+                .diskCacheFileNameGenerator(new HashCodeFileNameGenerator())
+                .build();//
+        ImageLoader.getInstance().init(config);
+    }
+}

+ 255 - 0
app/src/main/java/com/sunwin/visitorapp/view/FaceDetecterView.java

@@ -0,0 +1,255 @@
+package com.sunwin.visitorapp.view;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.hardware.Camera;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.annotation.RequiresApi;
+
+import com.sunwin.visitorapp.BaseApplication;
+import com.sunwin.visitorapp.R;
+import com.sunwin.visitorapp.camera.CameraHolder;
+import com.sunwin.visitorapp.camera.CameraSurfaceView;
+import com.sunwin.visitorapp.face.FRAbsLoop;
+import com.sunwin.visitorapp.face.FRAbsLoopFactory;
+import com.sunwin.visitorapp.model.OpenDoorByFaceNetResultInfo;
+import com.sunwin.visitorapp.model.bus.DescEvent;
+import com.sunwin.visitorapp.utils.Constant;
+import com.sunwin.visitorapp.utils.LogUtil;
+import com.sunwin.visitorapp.utils.SharePrefenceUtils;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.concurrent.CountDownLatch;
+
+import butterknife.Unbinder;
+
+
+public class FaceDetecterView extends RelativeLayout {
+
+    private static final String TAG = FaceDetecterView.class.getSimpleName();
+
+    private Unbinder unbinder;
+    Activity currenActivity;
+
+    CameraSurfaceView mSurfaceView;
+    TextView mShowVerifaceHint;
+
+    private boolean bopen = false;
+    private boolean bStoped = false;
+    private CountDownLatch countdownLatch = new CountDownLatch(1);
+    private boolean isIDNumberOpen = SharePrefenceUtils.getBoolean("isIdNumberOpenDoor", true);
+
+    /**
+     * 通讯标记
+     */
+    private static final int WHAT_FIND_CARD = 10098;
+    private static final int WHAT_READ_CARD = 10099;
+    /**
+     * 是否需要读取指纹
+     */
+    private boolean mNeedFP = false;
+
+    /**
+     * 身份证线程运行控制标记
+     */
+    private boolean mRunFlag = false;
+    private volatile boolean isCanFlush = true;
+
+
+    private CameraHolder cameraHolder;
+    private boolean isTakePic = false;
+
+    public FaceDetecterView(Context context) {
+        super(context);
+        initData();
+    }
+
+    public FaceDetecterView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initData();
+    }
+
+    public FaceDetecterView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initData();
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+    public FaceDetecterView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        initData();
+    }
+
+    public void startVerify() {
+        mFRAbsLoop.startVerify();
+    }
+
+    public void initData() {
+        EventBus.getDefault().register(this);
+    }
+
+    public void onResume() {
+        startCamera();
+    }
+
+    public void stopCamera() {
+        cameraHolder.stopCamera();
+    }
+
+    public void startCamera() {
+        cameraHolder.reStartCamera();
+    }
+
+    public void onPause() {
+
+    }
+
+    public void onDestroy() {
+        mFRAbsLoop.shutdown();
+        BaseApplication.getSystemTTS().stopSpeak();
+        EventBus.getDefault().unregister(this);
+    }
+
+
+    FRAbsLoop mFRAbsLoop = null;
+
+    public void config(Activity activity, Listener listener) {
+        View view = View.inflate(activity, R.layout.view_face_detecter, this);
+        mSurfaceView = view.findViewById(R.id.show_surfaceview);
+        mShowVerifaceHint = view.findViewById(R.id.show_veriface_hint);
+
+        currenActivity = activity;
+        mListener = listener;
+        cameraHolder = new CameraHolder() {
+            @Override
+            public Object onPreview(byte[] data, int width, int height, int format, long timestamp) {
+                LogUtil.v(TAG, "onPreview mListener = " + mListener);
+                final Rect[] rects = mFRAbsLoop.onPreview(data, width, height, format, timestamp);
+                if (rects != null && rects.length > 0) {
+                    //检测到人脸,隐藏待机页面
+                    mListener.detectedFace();
+                } else {
+//                    LogUtil.d(TAG, "没检测到人脸");
+                }
+                return rects;
+            }
+
+
+        };
+
+
+        //采用前置摄像头
+        if (Constant.isFrontCamera) {
+            cameraHolder.configCamera(Camera.CameraInfo.CAMERA_FACING_FRONT, Constant.rotate, true);
+        } else {
+            cameraHolder.configCamera(Camera.CameraInfo.CAMERA_FACING_BACK, Constant.rotate, true);
+        }
+
+        cameraHolder.configView(activity.getWindowManager(), mSurfaceView);
+
+        mFRAbsLoop = new FRAbsLoopFactory().createFRAblLoop(currenActivity, Constant.NumerValue.FRABS_TYPE);
+        // 出现重复启动,导致崩溃
+        mFRAbsLoop.start();
+
+
+    }
+
+//    public void takePic() {
+//        if (cameraHolder != null && !isTakePic) {
+//            isTakePic = true;
+//            cameraHolder.mCamera.takePicture(null, null, mJpeg);
+//        } else {
+//            ToastUtils.showToast("操作太频繁");
+//        }
+//
+//    }
+
+//    private Camera.PictureCallback mJpeg = new Camera.PictureCallback() {
+//        @Override
+//        public void onPictureTaken(final byte[] data, Camera camera) {
+//            new Thread(new Runnable() {
+//                @Override
+//                public void run() {
+//                    try {
+//                        //将照片改为竖直方向
+//                        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
+//                        Matrix matrix = new Matrix();
+//                        matrix.preRotate(Constant.rotate);
+//                        bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+//                        final Bitmap finalBitmap = bitmap;
+//                        currenActivity.runOnUiThread(new Runnable() {
+//                            @Override
+//                            public void run() {
+//                                mListener.takePic(finalBitmap);
+//                            }
+//                        });
+//                        stopCamera();
+//                        startCamera();
+//                        isTakePic = false;
+//                    } catch (Exception e) {
+//                        e.printStackTrace();
+//                    }
+//
+//                }
+//            }).start();
+//        }
+//    };
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void onEventBack(DescEvent event) {
+        if (mShowVerifaceHint != null) {
+            mShowVerifaceHint.setText(event.getDesc());
+        }
+        if (event.getDesc().equalsIgnoreCase(DescEvent.DESC_FR_SUCC)) {
+            isCanFlush = true;
+            BaseApplication.getSystemTTS().stopSpeak();
+            LogUtil.e(TAG, "接收成功信息");
+            if (mListener!= null){
+
+                mListener.detectedUser(event.getOpenDoorByFaceNetResultInfo());
+            }
+        } else if (event.getDesc().equalsIgnoreCase(DescEvent.DESC_FR_NET_ERROR)) {
+            BaseApplication.getSystemTTS().stopSpeak();
+        } else if (event.getDesc().equalsIgnoreCase(DescEvent.DESC_FR_TIP)) {
+        } else if (event.getDesc().equalsIgnoreCase(DescEvent.DESC_FR_TIP_2)) {
+        } else if (event.getDesc().equalsIgnoreCase(DescEvent.DESC_FR_NO_BODY)) {
+            isCanFlush = true;
+            BaseApplication.getSystemTTS().playText(event.getDesc());
+            mListener.detecterFail(event.getDesc());
+        } else if (event.getDesc().equalsIgnoreCase(DescEvent.DESC_FR_FROCUSE_EYE)) {
+            BaseApplication.getSystemTTS().playText(event.getDesc());
+        }
+    }
+
+//    public void onEventMainThread(String st) {
+//        if ("isUpload".equals(st)) {
+//            mShowVerifaceHint.setText("后台正在更新数据");
+//        } else if ("isUploadFinish".equals(st)) {
+//            mShowVerifaceHint.setText("数据已更新");
+//        }
+//    }
+
+    private Listener mListener;
+
+    public interface Listener {
+        //检测到人脸
+        void detectedFace();
+
+        void detectedUser(OpenDoorByFaceNetResultInfo openDoorByFaceNetResultInfo);
+
+        void detecterFail(String st);
+
+        void takePic(Bitmap bitmap);
+    }
+
+}

+ 29 - 0
app/src/main/java/com/zkteco/android/IDReader/IDPhotoHelper.java

@@ -0,0 +1,29 @@
+package com.zkteco.android.IDReader;
+
+import android.graphics.Bitmap;
+
+
+/**
+ * Created by scarx on 2015/12/3.
+ */
+public class IDPhotoHelper {
+    public static Bitmap Bgr2Bitmap(byte[] bgrbuf)
+    {
+        int width = WLTService.imgWidth;
+        int height = WLTService.imgHeight;
+        Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+        int row = 0, col = width-1;
+        for (int i = bgrbuf.length-1; i >= 3; i -= 3) {
+            int color = bgrbuf[i] & 0xFF;
+            color += (bgrbuf[i-1] << 8) & 0xFF00;
+            color += ((bgrbuf[i-2]) << 16) & 0xFF0000;
+            bmp.setPixel(col--, row, color);
+            if (col < 0) {
+                col = width-1;
+                row++;
+            }
+        }
+        return bmp;
+    }
+	
+}

+ 15 - 0
app/src/main/java/com/zkteco/android/IDReader/WLTService.java

@@ -0,0 +1,15 @@
+package com.zkteco.android.IDReader;
+/**
+ * Created by tianfeng
+ * 中控的身份证读取
+ */
+public class WLTService {
+    static {
+        System.loadLibrary("wlt2bmp");
+        System.loadLibrary("zkwltdecode");
+    }
+    public static int imgWidth = 102;
+    public static int imgHeight = 126;
+    public static int imgLength = 3 * 102 * 126;
+    public static  native int wlt2Bmp(byte[] inbuf, byte[] outbuf);
+}

+ 25 - 12
app/src/main/res/layout/activity_id_card_reg.xml

@@ -11,10 +11,12 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
+        android:layout_marginLeft="120dp"
+        android:layout_marginTop="60dp"
+        android:layout_marginRight="120dp"
+        android:layout_marginBottom="40dp"
         android:orientation="horizontal">
-        <!--
-      android:layout_margin="120dp"
-              -->
+
         <LinearLayout
             android:layout_width="0dp"
             android:layout_height="wrap_content"
@@ -53,7 +55,6 @@
             android:layout_height="match_parent"
             android:layout_marginLeft="50dp"
             android:layout_weight="2"
-            android:padding="50dp"
             android:background="@color/color_efeeee"
             android:orientation="horizontal">
 
@@ -61,38 +62,50 @@
                 android:id="@+id/ll_id_card_reg"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
+
+                android:layout_margin="50dp"
                 android:orientation="vertical">
 
                 <ImageView
-                    android:layout_weight="1"
                     android:layout_width="match_parent"
                     android:layout_height="match_parent"
+                    android:layout_weight="1"
                     android:scaleType="fitXY"
-                    android:src="@mipmap/ic_launcher" />
+                    android:src="@mipmap/id_card" />
 
                 <TextView
-                    android:textSize="@dimen/sp_25"
-                    android:layout_marginTop="10dp"
-                    android:textColor="@color/color_333333"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_gravity="center_horizontal"
-                    android:text="请刷身份证" />
+                    android:layout_marginTop="10dp"
+                    android:text="请刷身份证"
+                    android:textColor="@color/color_333333"
+                    android:textSize="@dimen/sp_25" />
             </LinearLayout>
 
+            <include
+                android:id="@+id/faceDetecterView"
+                layout="@layout/view_face_detecter"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="gone" />
+
+
         </LinearLayout>
 
 
     </LinearLayout>
+
     <com.sunwin.visitorapp.view.MyGridView
         android:id="@+id/gridview"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingTop="10dp"
-        android:paddingBottom="10dp"
+        android:layout_marginLeft="120dp"
+        android:layout_marginRight="120dp"
         android:horizontalSpacing="60dp"
         android:numColumns="5"
         android:overScrollMode="never"
+        android:paddingBottom="10dp"
         android:scrollbars="none"
         android:verticalSpacing="5dp" />
 </LinearLayout>

+ 0 - 3
app/src/main/res/layout/activity_visitor_reg.xml

@@ -13,9 +13,6 @@
         android:layout_margin="120dp"
         android:layout_gravity="center_horizontal"
         android:orientation="horizontal">
-        <!--
-      android:layout_margin="120dp"
-              -->
         <LinearLayout
             android:layout_width="0dp"
             android:layout_height="wrap_content"

+ 27 - 0
app/src/main/res/layout/adapter_card.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/item_icon"
+            android:layout_width="100dp"
+            android:layout_height="100dp"
+            android:scaleType="fitXY"
+            android:src="@mipmap/ic_launcher" />
+
+
+
+        <TextView
+            android:id="@+id/item_desc"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_gravity="center_horizontal"
+            android:text="访客记录"
+            android:textColor="@color/color_333333"
+            android:textSize="@dimen/sp_20" />
+
+</LinearLayout>

+ 27 - 0
app/src/main/res/layout/view_face_detecter.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.sunwin.visitorapp.view.FaceDetecterView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+
+    <com.sunwin.visitorapp.camera.CameraSurfaceView
+        android:id="@+id/show_surfaceview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+
+    <TextView
+        android:id="@+id/show_veriface_hint"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="20dp"
+        android:shadowColor="@color/black"
+        android:shadowDx="3"
+        android:shadowDy="3"
+        android:shadowRadius="3"
+        android:text="@string/veriface_hint"
+        android:textColor="@android:color/white"
+        android:textSize="@dimen/sp_20" />
+</com.sunwin.visitorapp.view.FaceDetecterView>

BIN
app/src/main/res/mipmap-xhdpi/id_card.png


+ 0 - 1
app/src/main/res/values/strings.xml

@@ -4,5 +4,4 @@
     <string name="login_title">欢迎使用智能访客登记系统</string>
     <string name="login">登录</string>
     <string name="login_byself">自助登记</string>
-    <string name="app_title">访客登记一体机</string>
 </resources>

Some files were not shown because too many files changed in this diff