本地双录

通过阅读本文,您可以快速了解 IDRS Android SDK 本地双录的相关功能及调用方法。

创建项目工程

打开 Android Studio IDE,用 Android Studio 创建一个项目工程。

说明

Android Studio 下载地址请参见 Android Studio 下载文件归档

引入 SDK

在 build.gradle 中添加依赖。

dependencies {
    implementation ('com.mpaas.android:idrs-process:cp_change_23596.25')//本地双录能力库
    implementation 'com.aliyun.openservices:aliyun-log-android-sdk:2.6.2'//日志库
    implementation 'com.squareup.okhttp3:okhttp:3.11.0'//网络库
    implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0'//网络库
    implementation "com.alibaba:fastjson:1.2.83"//json库
}

ProcessSDK 功能

初始化 ProcessSDK

初始化方法仅对 ProcessSDK 初始化所在的 activity 进行前台监听,因此为了精确地计算使用时长,您可以在 Application onCreate 中调用初始化方法。

public class ApplicationDemo extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        //****
        IdrsAppLifecycle.init(this);
        //****
    }
}

在使用流程场景中初始化 ProcessSDK 的方法如下:

ProcessInitConfig config = new ProcessInitConfig();
config.processId = processId;
/**
 * 此处以把 AccessKey 和 AccessKeySecret 保存在环境变量为例说明。您也可以根据业务需要,保存到配置文件里
 * 强烈建议不要把 AccessKey 和 AccessKeySecret 保存到代码里,会存在密钥泄漏风险
 */
String appId = intent.getConfigStringExtra("appid"); // appId,在控制台申请的,和包名绑定
String ak = intent.getConfigStringExtra("securityAK"); 
String sk = intent.getConfigStringExtra("securitySK"); 
config.appId = appId;
config.ak = ak;
config.sk = sk;
config.uid = "test123"; // 账户系统的uid
config.fileDir = getExternalCacheDir().getAbsolutePath(); // 可选,用于指定缓存目录
File file = new File(config.fileDir);
if (!file.exists()) {
    file.mkdirs();
}
ProcessSdk.getInstance().init(RecorderActivity.this, config, new ProcessSdkCallback())

开启摄像头

MPCameraView cameraView = mProcessSdk.startCamera(this, new InitCallback() {
            @Override
            public void initSuccess(int i, int i1) {
                LogUtils.getInstance().debug(TAG, "startCamera success");
            }
});
// 添加到布局中
cameraParent.addView(mMPCameraView, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

切换摄像头

mProcessSdk.switchCamera();

开始流程

mProcessSdk.startFlow();

开始下一个检测项

mProcessSdk.startNextDetection();

上一章

mProcessSdk.executePrePhases();

重新检测当前项

mProcessSdk.repeatCurrentDetection();

重新播放当前章节

mProcessSdk.retryCurrenPhases();

暂停/恢复播放

mProcessSdk.flowPause();//播放暂停
mProcessSdk.flowResume();//恢复播放

清空默认路径下的视频及 meta/result 文件

mProcessSdk.clearCache();

销毁

请及时使用销毁、否则可能会影响下次初始化。

mProcessSdk.release();

ProcessSDK 回调

// 流程拉取回调,用于更新流程中的配置信息,包括名称、角色等
@Override
 public String onEditFlowJson(String json) {
     Log.i(TAG, "json: " + json);
     try {
         JSONObject jsonObject = new JSONObject(json);
         JSONObject dataObject = jsonObject.optJSONObject("Data");
         if (dataObject != null) {
             String contentStr = dataObject.optString("Content");
             if (contentStr != null) {
                 JSONObject contentJsonO = new JSONObject(contentStr);
                 JSONArray roleJsonArray = contentJsonO.optJSONArray("roles");
                 if (roleJsonArray != null) {
                     int size = roleJsonArray.length();
                     for (int index = size - 1; index >= 0; index--) {
                         roleJsonArray.remove(index);
                     }

                     List<RoleBean> roleBeanList = MainActivity.rolesCache;
                     for (RoleBean roleBean : roleBeanList) {
                         JSONObject roleItem = new JSONObject();
                         roleItem.put("id", roleBean.id);
                         roleItem.put("photo", roleBean.base64);
                         roleJsonArray.put(roleItem);
                     }
                 }

                 JSONArray phasesJsonArray = contentJsonO.optJSONArray("phases");
                 if (phasesJsonArray != null) {
                     for (int index = 0; index < phasesJsonArray.length(); index++) {
                         JSONObject itemJson = phasesJsonArray.optJSONObject(index);
                         if (itemJson != null) {
                             JSONObject voiceJson = itemJson.optJSONObject("voice");
                             if (voiceJson != null) {
                                 JSONObject ttsJSON = voiceJson.optJSONObject("tts");
                                 if (ttsJSON != null) {
                                     String ttsText = ttsJSON.optString("ttsText");
                                     if (!TextUtils.isEmpty(ttsText)) {
                                     // 示例,根据后台实际配置
                                         ttsText = ttsText.replaceAll("#twoName#", "");
                                         ttsText = ttsText.replaceAll("#oneName#",
                                                 "");
                                         ttsJSON.put("ttsText", ttsText);
                                     }
                                 }
                             }
                         }
                     }
                 }
                 dataObject.put("Content", contentJsonO.toString());
             }
         }

         return jsonObject.toString();
     } catch (Exception e) {

     }
     return null;
 }

// 检测结果回调
 @Override
 public void onDetectionFail(AllDataBean.PhasesBean.DetectionsBean currenDetection) {
     Log.d("mmm", currenDetection.getId() + "/" + "超时");
     setSkipVISIBLE();
     switch (currenDetection.getId()) {
         case "id_card_recognize":
             setCompleteViewError("识别身份证超时");
             break;

         case "sign_action_recognize":
             setCompleteViewError("识别签字动作超时");
             break;

         case "sign_classify_recognize":
             setCompleteViewError("识别手写签名超时");
             break;

         case "speech_word_detect":
             setCompleteViewError("识别激活词超时");
             break;
     }
 }

// 章节完成
 @Override
 public void onPhaseCompleted(AllDataBean.PhasesBean currenPhases) {
     Log.d("mmm", currenPhases.getId() + "/" + "章节完成");
 }

// 章节开始
 @Override
 public void onPhaseStarted(final AllDataBean.PhasesBean currenPhases) {
     setCompleteViewINVISIBLE();
     clearSurface();
     setSkipINVISIBLE();
     runOnUiThread(new Runnable() {
         @Override
         public void run() {
             text.setText(currenPhases.getVoice().tts.ttsText);
         }
     });
 }

// 整体流程完成
 @Override
 public void onFlowCompleted(List<UploadFileBean> allResultFiles) {
     Log.d("mmm", "最后一个阶段");
     runOnUiThread(new Runnable() {
         @Override
         public void run() {
             mLoadingView.setVisibility(View.GONE);
             findViewById(R.id.reupload).setVisibility(View.GONE);
             finishFlow();
             Toast.makeText(RecorderActivity.this, "流程结束", Toast.LENGTH_SHORT).show();
             finish();
         }
     });

 }

// 错误回调,错误码如下:
 @Override
 public void onError(final int code, final String msg) {
     LogUtils.getInstance().debug("mmmerror", "error: " + code + " " + msg);
     runOnUiThread(new Runnable() {
         @Override
         public void run() {
             mLoadingView.setVisibility(View.GONE);
             if (code == ProcessSdk.ERROR_TYPE_UPLOAD) { // 结果上传失败
                 findViewById(R.id.reupload).setVisibility(View.VISIBLE);
             } else if (code == ProcessSdk.ERROR_TYPE_IDRS // idrs初始化失败
                     || code == ProcessSdk.ERROR_TYPE_PROCESS_CONTENT // 流程内容获取失败
                     || code == ProcessSdk.ERROR_TYPE_RECORD_ERROR) { // 录制失败
                 disableAllBtn();
             }
             errorTextV.setText(errorTextV.getText() + "\n 错误:" + code + " " + msg);

         }
     });

 }

// 结果上传开始
 @Override
 public void onUploadStart() {
     runOnUiThread(new Runnable() {
         @Override
         public void run() {
             mLoadingView.setVisibility(View.VISIBLE);
             errorTextV.setText(errorTextV.getText() + "\n 开始上传");
         }
     });
 }

// 音频文件下载回调
 @Override
 public void onAudioDownloadProcess(final int process, final int index, final int size) {
     runOnUiThread(new Runnable() {
         @Override
         public void run() {
             if (mDownloadBeforeText == null) {
                 mDownloadBeforeText = errorTextV.getText().toString();
             }
             errorTextV.setText(mDownloadBeforeText + "\n音频下载进度:" + process + "% " + (index + 1) + "/" + size);
         }
     });
 }

// 检测成功回调
 @Override
 public boolean onDetectionSuccess(AllDataBean.PhasesBean.DetectionsBean currenDetection, final String result) {
     Log.d("mmm检测结果", currenDetection.getId() + "/" + result);

     switch (currenDetection.getId()) {
         case "id_card_recognize":
             setCompleteView(result);
             break;

         case "sign_action_recognize":
             setCompleteView("识别手写签字");
             break;

         case "sign_classify_recognize":
             setCompleteView("识别手写签名");
             break;

         case "speech_word_detect":
             setCompleteView(result);
             break;
         case "static_hand_recognize":
             if (!TextUtils.isEmpty(result)) {
                 if (result.contains("5")) {
                     runOnUiThread(new Runnable() {
                         @Override
                         public void run() {
                             errorTextV.setText(errorTextV.getText() + "\n 手势结果:" + result);
                             clearSurface();
                         }
                     });
                     return true;
                 }
             }
             break;
     }

     return false;
 }

// 开始检测回调
 @Override
 public void onStartDetection(AllDataBean.PhasesBean.DetectionsBean currenDetection) {
     //                Log.d("mmm开始检测", currenDetection.getId());
     setCompleteViewINVISIBLE();
     setSkipINVISIBLE();
     setErrorINVISIBLE();
     clearSurface();
     mCurrenDetection = currenDetection;
     runOnUiThread(new Runnable() {
         @Override
         public void run() {
             if (mCurrenDetection != null) {
                 errorTextV.setText(errorTextV.getText() + "\n 开始检测:" + mCurrenDetection.getId());
             }
         }
     });

 }

// 手势实时结果
 @Override
 public void onHandDetectUi(ArrayList<HandDetectionResult> results, int angle, int degree, boolean isstatic) {
     if (results != null) {
         Facemanager.getInstance().drawLines(results, angle, degree, isstatic);
     }
 }

// 人脸实时结果
 @Override
 public void onFaceDetectUi(DectetResult[] dectetResults) {
     Facemanager.getInstance().drawRect(dectetResults);
 }

// 人脸实时结果
 @Override
 public void onFaceRecognizeUi(ArrayList<DectetResult> dectetResults) {
     Facemanager.getInstance().drawResult(dectetResults, angle, degree, isAlive);
 }

// 活体实时结果
 @Override
 public void onFaceLiveStatusChange(boolean isError, String message) {
     Log.d("mmmcahgnge", isError + "/" + message);
     isAlive = !isError;
 }

 @Override
 public void onFaceNumChange(boolean isError, String message) {
     Log.d("mmmcahgnge", isError + "/" + message);
     //if (isError) {
     //    setErrorVISIBLE(message);
     //} else {
     //    setErrorINVISIBLE();
     //}
 }

// tts识别过程回调
 @Override
 public void onTTSProcess(final String text, final int curIndex, final String currentText) {
     runOnUiThread(new Runnable() {
         @Override
         public void run() {
             int index = curIndex;
             LogUtils.getInstance().debug(TAG, "ttsprocess: " + index);
             String textAll = text;
             if (!TextUtils.isEmpty(textAll)) {
                 SpannableString spannableString = new SpannableString(textAll);
                 if (index >= textAll.length() - 1) {
                     index = textAll.length();
                 }
                 spannableString.setSpan(new ForegroundColorSpan(Color.RED), 0, index, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
                 if (index < textAll.length() - 1) {
                     spannableString.setSpan(new ForegroundColorSpan(Color.WHITE), index + 1,
                             textAll.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
                 }
                 LogUtils.getInstance().debug(TAG, "ttsprocess: " + index + " " + spannableString.toString());
                 RecorderActivity.this.text.setText(spannableString);
             }
         }
     });

 }

// idrs sdk初始化成功回调
 @Override
 public void onInitSuccess() {
     runOnUiThread(new Runnable() {
         @Override
         public void run() {
             errorTextV.setText(errorTextV.getText() + "\n 初始化成功");
         }
     });

 }

混淆配置

ProcessSDK 中的混淆配置如下:

-dontoptimize
-keep class com.mpaas.idrs.** { *; }
-keep class com.mpaas.idrs.process.** { *; }

## alinn
-keep class com.taobao.android.alinn.** { *; }
-keep class com.taobao.android.alinnkit.net.**{*;}
-keep class com.taobao.android.alinnkit.core.**{*;}
-keep class com.taobao.android.alinnkit.entity.**{*;}
-keep class com.taobao.android.alinnkit.json.**{*;}
## ocr
-keep class com.ant.phone.xmedia.**{*;}
-keep class com.alipay.xmedia.cache.**{*;}
-keep class com.alipay.xmedia.common.**{*;}
-keep class com.alipay.xmedia.gles.**{*;}
-keep class com.alipay.xmedia.serviceapi.**{*;}
-keep class com.alipay.xmedia.video.**{*;}
## 激活词
-keep class com.alibaba.idst.nui.**{*;}

-keep class com.aliyun.sls.android.producer.* { *; }
-keep interface com.aliyun.sls.android.producer.* { *; }

-keep class com.mpaas.idrs.** { *;}

## alinn
-keep class com.taobao.** { *; }
## ocr
-keep class com.ant.phone.xmedia.**{*;}
-keep class com.alipay.xmedia.**{*;}

-keep class com.mpaas.ocr.** {*;}

-keep class com.mpaas.**{*;}

-keep class com.alibaba.** {*;}

-keep class okhttp3.** {*;}

-keep class xnn.** {*;}
-ignorewarnings