diff --git a/app/src/main/java/com/sw/platecabinet/activity/BaseActivity.kt b/app/src/main/java/com/sw/platecabinet/activity/BaseActivity.kt index f491f1a..286e662 100644 --- a/app/src/main/java/com/sw/platecabinet/activity/BaseActivity.kt +++ b/app/src/main/java/com/sw/platecabinet/activity/BaseActivity.kt @@ -424,7 +424,7 @@ abstract class BaseActivity : AppCompatActivity() { // private val initialDelay = 5 * 60 * 1000L // private val dealyMillis = 10 * 60 * 1000L - private val initialDelay = 1 * 60 * 1000L + private val initialDelay = 5 * 60 * 1000L private val dealyMillis = 5 * 60 * 1000L private var taskPageNo = 1 fun startFaceTask() { diff --git a/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt b/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt index 5698b67..8392492 100644 --- a/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt +++ b/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt @@ -58,6 +58,7 @@ import com.sw.platecabinet.utils.Debouncer import com.sw.platecabinet.utils.IntervalExecutor import com.sw.platecabinet.utils.PermissionHelper import com.sw.platecabinet.utils.PlateUtils +import com.sw.platecabinet.utils.SoundPoolUtil import com.sw.platecabinet.utils.SpTool import com.sw.platecabinet.utils.countDownByFlow import kotlinx.coroutines.Job @@ -151,6 +152,11 @@ class LoginByFaceActivity : BaseActivity(), // ) // } // } + + + val soundMap = hashMapOf() + soundMap["success"] = R.raw.success + SoundPoolUtil.getInstance().loadR(this, soundMap); } private fun checkCameraPermission() { @@ -246,13 +252,19 @@ class LoginByFaceActivity : BaseActivity(), recognizeViewModel.recognizeUserId.observe(this, Observer { result: CompareResult? -> loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}") - if (result?.faceEntity == null) { + val similar = result?.similar?:0f + binding.tvSimilarValue.text="$similar" + val similarPass = result?.isSimilarPass?:false + if (similarPass.not()) { loadLogInfo("collectFace,recognizeUserId 未识别到人脸信息,开始采集") isCollectFace = true collectFace() return@Observer } currentUserId = result?.faceEntity?.userName + if (tempUserId != currentUserId) { + SoundPoolUtil.getInstance().play("success", 0) + } // plateEnable = true openPlate(currentUserId) }) @@ -547,6 +559,7 @@ class LoginByFaceActivity : BaseActivity(), }, 1000) return } + binding.tvSimilarValue.text="" isCollectFace = false plateEnable = true collectCount = 0 diff --git a/app/src/main/java/com/sw/platecabinet/utils/L.java b/app/src/main/java/com/sw/platecabinet/utils/L.java new file mode 100644 index 0000000..768cafd --- /dev/null +++ b/app/src/main/java/com/sw/platecabinet/utils/L.java @@ -0,0 +1,57 @@ +package com.sw.platecabinet.utils; + +/** + * Log统一管理类 + */ +public class L { + + private L() { + /* cannot be instantiated */ + throw new UnsupportedOperationException("cannot be instantiated"); + } + + public static boolean isDebug = true;// 是否需要打印bug,可以在application的onCreate函数里面初始化 + private static final String TAG = "mzf"; + + // 下面四个是默认tag的函数 + public static void i(String msg) { + if (isDebug) + android.util.Log.i(TAG, msg); + } + + public static void d(String msg) { + if (isDebug) + android.util.Log.d(TAG, msg); + } + + public static void e(String msg) { + if (isDebug) + android.util.Log.e(TAG, msg); + } + + public static void v(String msg) { + if (isDebug) + android.util.Log.v(TAG, msg); + } + + // 下面是传入自定义tag的函数 + public static void i(String tag, String msg) { + if (isDebug) + android.util.Log.i(tag, msg); + } + + public static void d(String tag, String msg) { + if (isDebug) + android.util.Log.d(tag, msg); + } + + public static void e(String tag, String msg) { + if (isDebug) + android.util.Log.e(tag, msg); + } + + public static void v(String tag, String msg) { + if (isDebug) + android.util.Log.v(tag, msg); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sw/platecabinet/utils/SoundPoolUtil.java b/app/src/main/java/com/sw/platecabinet/utils/SoundPoolUtil.java new file mode 100644 index 0000000..707e2a1 --- /dev/null +++ b/app/src/main/java/com/sw/platecabinet/utils/SoundPoolUtil.java @@ -0,0 +1,427 @@ +package com.sw.platecabinet.utils; + +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.SoundPool; +import android.os.Build; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class SoundPoolUtil { + + private static final String TAG = SoundPoolUtil.class.getSimpleName(); + private static SoundPoolUtil mSound; + private SoundPool mSoundPool; + private boolean isLoadC = false; + private Map idCache; + private List sidCache; + + public SoundPoolUtil() { + idCache = new HashMap<>(); + sidCache = new ArrayList<>(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + AudioAttributes aab = new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .setUsage(AudioAttributes.USAGE_MEDIA) + .build(); + mSoundPool = new SoundPool.Builder() + .setMaxStreams(10) + .setAudioAttributes(aab) + .build(); + } else { + mSoundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 8); + } +// mSoundPool = new SoundPool(60, AudioManager.USE_DEFAULT_STREAM_TYPE, 7); + mSoundPool.setOnLoadCompleteListener(new MyOnLoadCompleteListener()); + } + + public static SoundPoolUtil getInstance() { + + synchronized (SoundPoolUtil.class) { + if (mSound == null) { + mSound = new SoundPoolUtil(); + } + } + return mSound; + } + + private int loadCompleteSize = 0; + private LoadCompletion completionListener; + + public void setCompletionListener(LoadCompletion listener) { + this.completionListener = listener; + } + + private class MyOnLoadCompleteListener implements SoundPool.OnLoadCompleteListener { + + @Override + public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { + loadCompleteSize++; + + L.e("loadSize" + loadSize + "===" + loadCompleteSize); + if (loadSize == loadCompleteSize) { + isLoadC = true; + if (completionListener != null) { + completionListener.onCompletion(); + } + } + } + } + + + /** + * 加载指定资源 + * + * @param name + * @param path + */ + public void loadR(String name, String path) { + if (checkSoundPool()) { + if (!idCache.containsKey(name)) { + idCache.put(name, mSoundPool.load(path, 1)); + } + } + } + + private int loadSize = 0; + + /** + * 加载指定路径列表的资源 + * + * @param map + */ + public void loadR(Map map) { + loadSize = map.size(); + Set> entries = map.entrySet(); + for (Map.Entry entry : entries) { + String key = entry.getKey(); + if (checkSoundPool()) { + if (!idCache.containsKey(key)) { + idCache.put(key, mSoundPool.load(entry.getValue(), 1)); + } + } + } + } + + /** + * 加载指定AssetFileDescriptor的资源 + * + * @param name + * @param afd + */ + public void loadRF(String name, AssetFileDescriptor afd) { + if (checkSoundPool()) { + if (!idCache.containsKey(name)) { + idCache.put(name, mSoundPool.load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), 1)); + } + } + } + + /** + * 加载指定AssetFileDescriptor列表的资源 + * + * @param map + */ + public void loadRF(Map map) { + Set> entries = map.entrySet(); + for (Map.Entry entry : entries) { + String key = entry.getKey(); + if (checkSoundPool()) { + if (!idCache.containsKey(key)) { + idCache.put(key, mSoundPool.load(entry.getValue().getFileDescriptor(), entry.getValue().getStartOffset(), entry.getValue().getLength(), 1)); + } + } + } + } + + /** + * 加载指定列表资源 + * + * @param context + * @param map + */ + public void loadR(Context context, Map map) { + loadSize = map.size(); + Set> entries = map.entrySet(); + for (Map.Entry entry : entries) { + String key = entry.getKey(); + if (checkSoundPool()) { + + if (!idCache.containsKey(key)) { + idCache.put(key, mSoundPool.load(context, entry.getValue(), 1)); + } + } + } + } + + /** + * 加载单个音频 + * + * @param context + * @param name + * @param res + */ + public void loadR(Context context, String name, int res) { + if (checkSoundPool()) { + if (!idCache.containsKey(name)) { + idCache.put(name, mSoundPool.load(context, res, 1)); + } + } + } + + /** + * 播放指定音频,并返用于停止、暂停、恢复的StreamId + * + * @param name + * @param times + * @return + */ + public int play(String name, int times) { + L.e(String.format("play %s times=%s", name, times)); + return this.play(name, 1, 1, 1, times, 1); + } + + /** + * 播放指定音频,并指定播放次数和频率 + * + * @param name + * @param times + * @param rate + * @return + */ + public int play(String name, int times, int rate) { + return this.play(name, 1, 1, 1, times, rate); + } + + /** + * 播放指定音频,并指定优先级和播放频率 + * + * @param name + * @param property + * @param times + * @param rate + * @return + */ + public int play(String name, int property, int times, int rate) { + return this.play(name, 1, 1, property, times, rate); + } + + /** + * 播放指定音频,并指定左右声道、优先级、播放次数、播放频率 + * + * @param name + * @param leftVolume + * @param rightVolume + * @param property + * @param times + * @param rate + * @return + */ + public int play(String name, float leftVolume, float rightVolume, int property, int times, int rate) { + int streamId = -1; + if (checkSoundPool()) { + L.d(TAG, "play: " + name); + if (idCache.containsKey(name) && isLoadC) { + L.d(TAG, "name:" + idCache.get(name)); + streamId = mSoundPool.play(idCache.get(name), leftVolume, rightVolume, property, times, rate); + L.d(TAG, "streadmId:" + streamId); + sidCache.add(streamId); + } + } + return streamId; + } + + /** + * 播放指定列表的音频,并返回并返用于停止、暂停、恢复的StreamId列表 + * + * @param names + * @param times + * @return + */ + public List play(List names, int times) { + + return this.play(names, 1, 1, 1, times, 1); + } + + /** + * 播放指定列表的音频,并返回并返用于停止、暂停、恢复的StreamId列表,指定次数和频率 + * + * @param names + * @param times + * @param rate + * @return + */ + public List play(List names, int times, int rate) { + + return this.play(names, 1, 1, 1, times, rate); + } + + /** + * 播放指定列表的音频,并返回并返用于停止、暂停、恢复的StreamId列表,指定所有参数 + * + * @param names + * @param leftVolume + * @param rightVolume + * @param property + * @param times + * @param rate + * @return + */ + public List play(List names, int leftVolume, int rightVolume, int property, int times, int rate) { + List streamIds = new ArrayList<>(); + if (checkSoundPool()) { + for (String name : names) { + if (idCache.containsKey(name) && isLoadC) { + int a = mSoundPool.play(idCache.get(name), leftVolume, rightVolume, property, times, rate); + streamIds.add(a); + sidCache.add(a); + } + } + } + return streamIds; + } + + /** + * 停止指定id音频 + */ + public void stop(int r) { + if (checkSoundPool()) { + mSoundPool.stop(r); + } + } + + /** + * 停止指定列表音频 + */ + public void stopAll() { + if (checkSoundPool()) { + for (int r : sidCache) { + mSoundPool.stop(r); + } + } + } + + /** + * 暂停指定音效 + * + * @param r + */ + public void pause(int r) { + if (checkSoundPool()) { + mSoundPool.pause(r); + } + } + + /** + * 暂停指定列表音频 + * + * @param list + */ + public void pause(List list) { + if (checkSoundPool()) { + for (int r : list) { + mSoundPool.pause(r); + } + } + } + + /** + * 暂停所有音效 + */ + public void pauseAll() { + mSoundPool.autoPause(); + } + + /** + * 恢复指定音频播放 + * + * @param r + */ + public void resume(int r) { + if (checkSoundPool()) { + mSoundPool.resume(r); + } + } + + /** + * 恢复指定列表的音频 + * + * @param list + */ + public void resume(List list) { + if (checkSoundPool()) { + for (int r : list) { + mSoundPool.resume(r); + } + } + } + + /** + * 恢复所有暂停的音频 + */ + public void resumeAll() { + if (checkSoundPool()) { + mSoundPool.autoResume(); + } + } + + /** + * 卸载指定音频 + * + * @param name + */ + public void unLoad(String name) { + if (checkSoundPool()) { + if (idCache.containsKey(name)) { + mSoundPool.unload(idCache.get(name)); + idCache.remove(name); + } + } + } + + /** + * 卸载指定列表的音频 + * + * @param names + */ + public void unLoad(List names) { + if (checkSoundPool()) { + for (String name : names) { + if (idCache.containsKey(name)) { + mSoundPool.unload(idCache.get(name)); + idCache.remove(name); + } + } + } + } + + + /** + * 释放所有资源,如果想继续播放,需要重新加载资源 + */ + public void release() { + if (checkSoundPool()) { + mSound = null; + mSoundPool.release(); + idCache.clear(); + } + } + + private boolean checkSoundPool() { + if (mSoundPool != null) { + return true; + } + return false; + } + + public interface LoadCompletion { + void onCompletion(); + } +} + diff --git a/app/src/main/res/layout/activity_login_face.xml b/app/src/main/res/layout/activity_login_face.xml index b921499..81967a0 100644 --- a/app/src/main/res/layout/activity_login_face.xml +++ b/app/src/main/res/layout/activity_login_face.xml @@ -64,6 +64,16 @@ android:textSize="36sp" android:textStyle="bold" /> + + diff --git a/app/src/main/res/raw/success.wav b/app/src/main/res/raw/success.wav new file mode 100644 index 0000000..d89f5c3 Binary files /dev/null and b/app/src/main/res/raw/success.wav differ diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java index d075ffb..ffd3b0e 100644 --- a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java +++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java @@ -5,6 +5,7 @@ import android.hardware.Camera; import android.os.Handler; import android.os.Looper; import android.util.Log; +import android.widget.Toast; import androidx.annotation.IntDef; import androidx.annotation.NonNull; @@ -18,6 +19,7 @@ import com.arcsoft.face.ImageQualitySimilar; import com.arcsoft.face.LivenessInfo; import com.arcsoft.face.MaskInfo; import com.arcsoft.face.enums.ExtractType; +import com.sw.plate.App; import com.sw.plate.utils.L; import com.sw.plate.utils.arcface.FaceRectTransformer; import com.sw.plate.utils.arcface.face.constants.LivenessType; @@ -653,11 +655,33 @@ public class FaceHelper implements FaceListener { } private int failCount = 0; + private long startTime = 0; private void searchFace(final FaceFeature faceFeature, final Integer trackId) { CompareResult compareResult = FaceServer.getInstance().searchFaceFeature(faceFeature, frEngine); if (compareResult == null || compareResult.getFaceEntity() == null) { + if (startTime == 0) { + failCount = 0; + startTime = System.currentTimeMillis(); + } + if (System.currentTimeMillis() - startTime > 10*1000) { + failCount = 0; + startTime = System.currentTimeMillis(); + } failCount++; - Log.d(TAG, "collectFace,searchFace查询失败"+failCount+"次"); + float similar; + if (compareResult != null) { + similar = compareResult.getSimilar(); + } else { + similar = 0f; + } + new Handler(Looper.getMainLooper()).post(()-> { + try { + Toast.makeText(App.getContext(),"识别失败"+failCount+"次,similar="+similar, Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + Log.d(TAG, "collectFace,searchFace查询失败"+failCount+"次,similar="+similar); if (failCount >= 2) { recognizeCallback.onRecognized(null, LivenessInfo.UNKNOWN, false); return; @@ -667,6 +691,7 @@ public class FaceHelper implements FaceListener { } compareResult.setTrackId(trackId); boolean pass = compareResult.getSimilar() > recognizeConfiguration.getSimilarThreshold(); + compareResult.setSimilarPass(pass); Log.d(TAG, "collectFace,searchFace: pass="+pass+",similar="+compareResult.getSimilar()+",threshold="+recognizeConfiguration.getSimilarThreshold()); recognizeCallback.onRecognized(compareResult, getRecognizeInfo(recognizeInfoMap, trackId).getLiveness(), pass); if (pass) { diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/CompareResult.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/CompareResult.java index 2d33f8b..f78d7c2 100644 --- a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/CompareResult.java +++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/CompareResult.java @@ -10,6 +10,16 @@ public class CompareResult { private int compareCode; private long cost; + private boolean similarPass; + + public void setSimilarPass(boolean similarPass) { + this.similarPass = similarPass; + } + + public boolean isSimilarPass() { + return similarPass; + } + public CompareResult(FaceEntity faceEntity, float similar) { this.faceEntity = faceEntity; this.similar = similar; diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/viewmodel/RecognizeViewModel.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/viewmodel/RecognizeViewModel.java index 5550152..dd01899 100644 --- a/lib_face/src/main/java/com/sw/plate/utils/arcface/viewmodel/RecognizeViewModel.java +++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/viewmodel/RecognizeViewModel.java @@ -505,7 +505,7 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback { } } } else { - recognizeUserId.postValue(new CompareResult(null, -1f)); + recognizeUserId.postValue(compareResult); } }); }