Prechádzať zdrojové kódy

1.新增开锁逻辑
2.新增双人认证

JaycePC 7 mesiacov pred
rodič
commit
403a8f218c
45 zmenil súbory, kde vykonal 2362 pridanie a 547 odobranie
  1. 34 1
      HttpCoreLibrary/src/main/java/com/rc/httpcore/OkHttpUtils.java
  2. 42 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/bean/AddChemicalBean.java
  3. 22 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/client/HttpTool.java
  4. 1 2
      HttpCoreLibrary/src/main/java/com/rc/httpcore/client/retrofit/ChemicalRetrofit.kt
  5. 0 55
      HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/ParameterValidationInterceptor.kt
  6. 1 1
      HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/TokenHeaderInterceptor.kt
  7. 79 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/FaceCompare1Req.java
  8. 0 2
      RcCore/src/main/java/com/rc/core/log/RcLog.kt
  9. 4 0
      RcCore/src/main/java/com/rc/core/ui/activity/RcBaseActivity.kt
  10. 2 0
      RcCore/src/main/java/com/rc/core/ui/common/AbsUIDelegate.kt
  11. 2 0
      RcCore/src/main/java/com/rc/core/ui/common/IUIListener.kt
  12. 7 0
      RcCore/src/main/java/com/rc/core/ui/common/UIDelegateImpl.kt
  13. 4 0
      RcCore/src/main/java/com/rc/core/ui/fragment/RcBaseFragment.kt
  14. 6 0
      app/src/main/AndroidManifest.xml
  15. 0 1
      app/src/main/java/com/example/chemical/ui/MainActivity.kt
  16. 1 0
      app/src/main/java/com/example/chemical/ui/fragments/InventoryIedgerFragment.kt
  17. 30 6
      app/src/main/java/com/example/chemical/ui/plan/PlanAddActivity.java
  18. 2 0
      app/src/main/java/com/example/chemical/ui/plan/PlanAddActivityHelp.java
  19. 11 0
      app/src/main/java/com/example/chemical/ui/plan/room/bean/locker/HxpCabinetAdminVo.java
  20. 15 0
      app/src/main/java/com/example/chemical/ui/plan/room/bean/locker/HxpCabinetLockVo.java
  21. 2 2
      app/src/main/java/com/example/chemical/ui/plan/room/dao/HxpDoorDAO.java
  22. 111 0
      app/src/main/java/com/example/chemical/ui/plan/save_list/SaveListActivity.java
  23. 140 0
      app/src/main/java/com/example/chemical/ui/plan/save_list/SaveListAdapter.java
  24. 61 0
      app/src/main/java/com/example/chemical/ui/plan/save_list/SaveListBean.java
  25. 214 27
      app/src/main/java/com/example/chemical/ui/plan/unlock/UnlockActivity.java
  26. 299 0
      app/src/main/java/com/example/chemical/ui/plan/unlock/UnlockActivityHelp.java
  27. 126 14
      app/src/main/java/com/example/chemical/ui/plan/unlock/UnlockAdapter.java
  28. 95 95
      app/src/main/java/com/example/chemical/ui/verify/DoubleVerifyActivity.java
  29. 254 0
      app/src/main/java/com/example/chemical/ui/verify/DoubleVerifyActivityHelp.java
  30. 0 225
      app/src/main/java/com/example/chemical/ui/verify/fragment/FaceDetectFragment.java
  31. 1 1
      app/src/main/java/com/example/chemical/ui/verify/fragment/DetectType.java
  32. 231 0
      app/src/main/java/com/example/chemical/ui/verify/include/FaceDetectView.java
  33. 11 0
      app/src/main/res/drawable/bg_verify_nt.xml
  34. 1 1
      app/src/main/res/layout/activity_add.xml
  35. 49 37
      app/src/main/res/layout/activity_double_verify.xml
  36. 140 0
      app/src/main/res/layout/activity_save_list.xml
  37. 37 13
      app/src/main/res/layout/activity_unlock.xml
  38. 13 0
      app/src/main/res/layout/fragment_card_detect.xml
  39. 55 63
      app/src/main/res/layout/item_door_unlock.xml
  40. 258 0
      app/src/main/res/layout/item_save_list.xml
  41. BIN
      app/src/main/res/mipmap-xhdpi/icon_gzxx_cw.png
  42. BIN
      app/src/main/res/mipmap-xhdpi/icon_gzxx_xz.png
  43. 0 0
      app/src/main/res/mipmap-xhdpi/icon_gzxx_zh.png
  44. BIN
      app/src/main/res/mipmap-xhdpi/qr.png
  45. 1 1
      app/src/main/res/values/themes.xml

+ 34 - 1
HttpCoreLibrary/src/main/java/com/rc/httpcore/OkHttpUtils.java

@@ -1,9 +1,18 @@
 package com.rc.httpcore;
 
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.rc.httpcore.vo.request.FaceCompare1Req;
+
+import java.io.File;
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 import okhttp3.Call;
 import okhttp3.MediaType;
+import okhttp3.MultipartBody;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
 import okhttp3.RequestBody;
@@ -12,7 +21,7 @@ import okhttp3.Response;
 public class OkHttpUtils {
 
     private static final OkHttpClient client = HttpClient.INSTANCE.buildHttpClient();
-    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+    private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
 
     private OkHttpUtils() {
     }
@@ -36,4 +45,28 @@ public class OkHttpUtils {
         Call call = client.newCall(request);
         return call.execute();
     }
+
+    public static Response postFileAndJson(String url, File file, String subjectId, FaceCompare1Req faceCompare1Req) throws IOException {
+        RequestBody fileRequestBody = RequestBody.create(MultipartBody.FORM, file);
+        MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), fileRequestBody);
+        List<String> doorIds = faceCompare1Req.getDoorIds();
+        StringBuilder doorIdsSb = new StringBuilder();
+        for (int i = 0; i < doorIds.size(); i++) {
+            if (i > 0) doorIdsSb.append(",");
+            doorIdsSb.append(doorIds.get(i));
+        }
+        String doorIdsSbs = doorIdsSb.toString();
+        RequestBody requestBody = new MultipartBody.Builder()
+                .setType(MultipartBody.FORM)
+                .addPart(filePart)
+                .addFormDataPart("doorIds", doorIdsSbs)
+                .addFormDataPart("subId", subjectId)
+                .build();
+
+        Request request = new Request.Builder()
+                .url(url)
+                .post(requestBody)
+                .build();
+        return client.newCall(request).execute();
+    }
 }

+ 42 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/bean/AddChemicalBean.java

@@ -0,0 +1,42 @@
+package com.rc.httpcore.bean;
+
+public class AddChemicalBean {
+    // ("化学平id")
+    private long chemicalId;
+    // ("化学品类别(参考字典表)")
+    private String chemicalCategory;
+    // ("化学品级别:1是管控,2是非管控")
+    private int chemicalLevel;
+    // ("创建时间")
+    private String createTime;
+    // ("化学品名称")
+    private String chemicalName;
+    // ("化学品类别")
+    private String chemicalCategoryName;
+    // ("化学品级别:1是管控,2是非管控")
+    private String chemicalLevelName;
+    // ("cas编号")
+    private String casNum;
+    // ("纯度")
+    private String chemicalPurity;
+    // ("密度")
+    private double chemicalDensity;
+    // ("化学品别名")
+    private String anotherName;
+    // ("存放规定单位")
+    private String depositUnit;
+    // ("规格值")
+    private double specNum;
+    // ("规格单位")
+    private String specUnit;
+    // ("包装值")
+    private double packNum;
+    // ("包装单位")
+    private String packUnit;
+    // 厂家
+    private String factory;
+    // 净含量
+    private double netWt;
+    // 重量
+    private String weight;
+}

+ 22 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/client/HttpTool.java

@@ -3,10 +3,15 @@ package com.rc.httpcore.client;
 import android.net.Uri;
 import android.util.Log;
 
+import com.blankj.utilcode.util.GsonUtils;
 import com.blankj.utilcode.util.LogUtils;
 import com.rc.httpcore.HttpConfig;
 import com.rc.httpcore.OkHttpUtils;
+import com.rc.httpcore.bean.AddChemicalBean;
+import com.rc.httpcore.bean.HxpChemicalVo;
+import com.rc.httpcore.vo.request.FaceCompare1Req;
 
+import java.io.File;
 import java.io.IOException;
 
 import okhttp3.Response;
@@ -40,4 +45,21 @@ public final class HttpTool {
         return OkHttpUtils.getSync(HttpConfig.Companion.getAPI_BASE_URL() + "chemical/aio/searchChemica?searchValue=" + searchValue + "&chemicalLevel=" + chemicalLevel);
     }
 
+    /**
+     * 入库
+     */
+    public static Response addChemical(AddChemicalBean addChemicalBean) throws IOException {
+        return OkHttpUtils.postSync(HttpConfig.Companion.getAPI_BASE_URL() + "chemical/aio/addStock", GsonUtils.toJson(addChemicalBean));
+    }
+
+    /**
+     * 双人验证
+     *
+     * @param photoFile       图片
+     * @param faceCompare1Req 支持多个柜门权限验证
+     */
+    public static Response checkUserFaceByPic1(File photoFile, String subjectId, FaceCompare1Req faceCompare1Req) throws IOException {
+        return OkHttpUtils.postFileAndJson(HttpConfig.Companion.getAPI_BASE_URL() + "chemical/aio/verify/checkUserFaceByPic1", photoFile, subjectId, faceCompare1Req);
+    }
+
 }

+ 1 - 2
HttpCoreLibrary/src/main/java/com/rc/httpcore/client/retrofit/ChemicalRetrofit.kt

@@ -249,6 +249,7 @@ class ChemicalRetrofit : ChemicalClient {
 
     override fun getRelList(subjectId: String): Observable<ChemicalInfoBean> {
         val json = "{\"subId\":$subjectId}"
+        LogUtils.d("获取实验室信息", json)
         val requestBody = RequestBody.create(MediaType.parse("application/json"), json)
         return apiService.getRelList(requestBody)
             .map { response ->
@@ -871,6 +872,4 @@ class ChemicalRetrofit : ChemicalClient {
                 return@map response.data
             }
     }
-
-
 }

+ 0 - 55
HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/ParameterValidationInterceptor.kt

@@ -19,60 +19,7 @@ class ParameterValidationInterceptor : Interceptor {
     @SuppressLint("LongLogTag")
     override fun intercept(chain: Interceptor.Chain): Response {
         val request = chain.request()
-//
-//        // 检查查询参数
-//        val originalUrl = request.url()
-//        Log.d(TAG, "Request parameters: $originalUrl")
-//        // 创建一个新的请求体,便于后续使用
-//        val requestBody = request.body()
-//        val buffer = Buffer()
-//        requestBody?.writeTo(buffer)
-//
-//        // 获取请求体的字符串形式(假设为 JSON)
-//        val requestBodyString = buffer.readUtf8()
-//
-//        // 建立一个标志来跟踪是否有空参数
-//        val hasEmptyParameter = StringBuilder()
-//        val queryParametersLog = StringBuilder()
-//        val bodyParametersLog = StringBuilder()
-//
-//        // 检查请求体(例如,POST 请求)
-//        if (request.method() == "POST" && requestBody != null) {
-//            try {
-//                val jsonParams = JSONObject(requestBodyString)
-//                jsonParams.keys().forEach { key ->
-//                    val value = jsonParams.optString(key)
-//                    if (value.isEmpty()) {
-//                        hasEmptyParameter.append("Body parameter '$key' is empty; ")
-//                    } else {
-//                        bodyParametersLog.append("Body parameter '$key': $value; ")
-//                    }
-//                }
-//            } catch (e: Exception) {
-//                Log.e(TAG, "Failed to parse request body as JSON: ${e.message}")
-//            }
-//        }
-//
-//        if (originalUrl.queryParameterNames().isNotEmpty()) {
-//            for (name in originalUrl.queryParameterNames()) {
-//                val value = originalUrl.queryParameter(name)
-//                if (value.isNullOrEmpty()) {
-//                    hasEmptyParameter.append("Query parameter '$name' is empty; ")
-//                } else {
-//                    queryParametersLog.append("Query parameter '$name': $value; ")
-//                }
-//            }
-//        }
         LogUtils.json(request)
-        // 打印所有请求参数(合并为一行)
-//        Log.d(TAG, "Request body: $bodyParametersLog")
-//        Log.d(TAG, "Request query: $queryParametersLog")
-
-
-//        // 打印空参数信息
-//        if (hasEmptyParameter.isNotEmpty()) {
-//            Log.e(TAG, "Request contains empty parameters: $hasEmptyParameter")
-//        }
 
         // 继续处理请求
         val response = chain.proceed(request)
@@ -81,8 +28,6 @@ class ParameterValidationInterceptor : Interceptor {
         val responseBody = response.body()
         val responseBodyString = responseBody?.string() ?: ""
 
-        // 打印返回值日志
-//        Log.d(TAG, "Response: $responseBodyString")
         LogUtils.d(response.toString(), responseBodyString)
         // 创建一个新的响应体,以便返回
         return response.newBuilder()

+ 1 - 1
HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/TokenHeaderInterceptor.kt

@@ -24,7 +24,7 @@ class TokenHeaderInterceptor : Interceptor {
         if (!HttpClient.token.isNullOrEmpty()) {
             requestBuilder.addHeader("authorization", HttpClient.token!!)
             Log.d("===========", HttpClient.token!!)
-            LogUtils.d("Token", HttpClient.token!!)
+//            LogUtils.d("Token", HttpClient.token!!)
         }
 
         requestBuilder

+ 79 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/FaceCompare1Req.java

@@ -0,0 +1,79 @@
+package com.rc.httpcore.vo.request;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class FaceCompare1Req {
+
+    public byte[] data; // 特征码
+    //    public File data; // 特征码
+    public String userId;
+    public String userIds;  //用户id区间值
+    public String subId;  //实验室id
+
+    public List<String> doorIds = new ArrayList<>();
+    public String waitId;
+    public String stockId;
+
+    public byte[] getData() {
+        return data;
+    }
+
+    public void setData(byte[] data) {
+        this.data = data;
+    }
+
+
+    public String getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(String userIds) {
+        this.userIds = userIds;
+    }
+
+    public String getSubId() {
+        return subId;
+    }
+
+    public void setSubId(String subId) {
+        this.subId = subId;
+    }
+
+    public List<String> getDoorIds() {
+        return doorIds;
+    }
+
+    public void setDoorIds(List<String> doorIds) {
+        this.doorIds = doorIds;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getWaitId() {
+        return waitId;
+    }
+
+    public void setWaitId(String waitId) {
+        this.waitId = waitId;
+    }
+
+    public String getStockId() {
+        return stockId;
+    }
+
+    public void setStockId(String stockId) {
+        this.stockId = stockId;
+    }
+}

+ 0 - 2
RcCore/src/main/java/com/rc/core/log/RcLog.kt

@@ -14,13 +14,11 @@ object RcLog {
     private val logger = Logger.getLogger(RcLog::class.java.name)
     var DE_BUG: Boolean = false
     fun warn(message: String, throwable: Throwable? = null) {
-        Log.d("====","DE_BUG$DE_BUG")
         if (!DE_BUG)return
         printLog(Level.WARNING, message, throwable)
     }
 
     fun info(message: String, throwable: Throwable? = null) {
-        Log.d("====","DE_BUG$DE_BUG")
         if (!DE_BUG)return
         printLog(Level.INFO, message, throwable)
     }

+ 4 - 0
RcCore/src/main/java/com/rc/core/ui/activity/RcBaseActivity.kt

@@ -103,6 +103,10 @@ abstract class RcBaseActivity<VB : ViewBinding> : AppCompatActivity(), IUIListen
         mUIDelegate.addDisposable(disposable)
     }
 
+    override fun delDisposable(disposable: Disposable) {
+        mUIDelegate.delDisposable(disposable)
+    }
+
     override fun createItemDecoration(): RecyclerView.ItemDecoration? {
         return mUIDelegate.createItemDecoration(this)
     }

+ 2 - 0
RcCore/src/main/java/com/rc/core/ui/common/AbsUIDelegate.kt

@@ -30,6 +30,8 @@ abstract class AbsUIDelegate {
 
     abstract fun addDisposable(disposable: Disposable)
 
+    abstract fun delDisposable(disposable: Disposable)
+
     abstract fun clearDisposable()
 
     abstract fun createItemDecoration(context: Context?): RecyclerView.ItemDecoration?

+ 2 - 0
RcCore/src/main/java/com/rc/core/ui/common/IUIListener.kt

@@ -20,6 +20,8 @@ interface IUIListener {
 
     fun addDisposable(disposable: Disposable)
 
+    fun delDisposable(disposable: Disposable)
+
     fun createItemDecoration(): RecyclerView.ItemDecoration?
 
 }

+ 7 - 0
RcCore/src/main/java/com/rc/core/ui/common/UIDelegateImpl.kt

@@ -65,6 +65,7 @@ class UIDelegateImpl : AbsUIDelegate() {
                     throwable.message!!
                 }
             }
+
             is SocketTimeoutException -> "请求超时,请稍后重试"
             is ConnectException -> "无法连接服务器,请检查网络"
             is HttpException -> "服务器繁忙,请稍后重试"
@@ -80,6 +81,12 @@ class UIDelegateImpl : AbsUIDelegate() {
         }
     }
 
+    override fun delDisposable(disposable: Disposable) {
+        mCompositeDisposable = (mCompositeDisposable ?: CompositeDisposable()).apply {
+            if (!isDisposed) remove(disposable)
+        }
+    }
+
     override fun clearDisposable() {
         mCompositeDisposable = mCompositeDisposable?.let {
             it.clear()

+ 4 - 0
RcCore/src/main/java/com/rc/core/ui/fragment/RcBaseFragment.kt

@@ -72,6 +72,10 @@ abstract class RcBaseFragment<VB : ViewBinding> :
         mUIDelegate.addDisposable(disposable)
     }
 
+    override fun delDisposable(disposable: Disposable) {
+        mUIDelegate.delDisposable(disposable)
+    }
+
     override fun onDestroyView() {
         mUIDelegate.clearDisposable()
         super.onDestroyView()

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

@@ -74,6 +74,12 @@
         android:usesCleartextTraffic="true"
         tools:targetApi="m">
         <activity
+            android:name=".ui.plan.save_list.SaveListActivity"
+            android:exported="false" />
+        <activity
+            android:name=".ui.verify.DoubleVerifyActivity"
+            android:exported="false" />
+        <activity
             android:name=".ui.plan.unlock.UnlockActivity"
             android:exported="false" />
         <activity

+ 0 - 1
app/src/main/java/com/example/chemical/ui/MainActivity.kt

@@ -678,7 +678,6 @@ class MainActivity : RcBaseActivity<ActivityMainBinding>() {
                                 else -> {
                                     //3条
                                     try {
-                                        LogUtils.d(669)
                                         map["mtypes"] = "0"
                                         map["faceList"] = faceList.toString()
                                         UiManager.switcher(

+ 1 - 0
app/src/main/java/com/example/chemical/ui/fragments/InventoryIedgerFragment.kt

@@ -21,6 +21,7 @@ import com.rc.core.ui.fragment.RcBaseFragment
 import com.rc.httpcore.bean.ChemicalTypeBean
 import com.rc.httpcore.bean.LockVoListBean
 import com.rc.httpcore.client.ApiRepository
+import io.reactivex.disposables.Disposable
 
 
 /**

+ 30 - 6
app/src/main/java/com/example/chemical/ui/plan/PlanAddActivity.java

@@ -19,6 +19,7 @@ import androidx.viewpager2.widget.ViewPager2;
 
 import com.blankj.utilcode.util.ActivityUtils;
 import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ThreadUtils;
 import com.blankj.utilcode.util.TimeUtils;
 import com.blankj.utilcode.util.ToastUtils;
 import com.bumptech.glide.Glide;
@@ -39,7 +40,9 @@ import com.example.chemical.ui.plan.room.dao.HxpLayerDAO;
 import com.example.chemical.ui.plan.unlock.UnlockActivity;
 import com.example.chemical.utils.UiManager;
 import com.rc.httpcore.HttpConfig;
+import com.rc.httpcore.bean.AddChemicalBean;
 import com.rc.httpcore.bean.ChemicalInfoBean;
+import com.rc.httpcore.client.HttpTool;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -47,6 +50,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 
+import okhttp3.Response;
+
 /**
  * 待录入页面
  */
@@ -123,12 +128,31 @@ public class PlanAddActivity extends BaseCountDownActivity<ActivityPlanAddBindin
         binding.subAdd.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                List<InventoryItemBean> inventoryItemBeans = hxpInventoryDAO.getAll();
-                if (null == inventoryItemBeans || inventoryItemBeans.isEmpty()) {
-                    ToastUtils.showLong("请录入化学品!");
-                    return;
-                }
-                ActivityUtils.startActivity(UnlockActivity.class);
+
+                ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
+                    @Override
+                    public Object doInBackground() throws Throwable {
+
+
+
+                        Response response = HttpTool.addChemical(new AddChemicalBean());
+                        LogUtils.json(response);
+                        LogUtils.d(response.body().string());
+                        return null;
+                    }
+
+                    @Override
+                    public void onSuccess(Object result) {
+
+                    }
+                });
+
+//                List<InventoryItemBean> inventoryItemBeans = hxpInventoryDAO.getAll();
+//                if (null == inventoryItemBeans || inventoryItemBeans.isEmpty()) {
+//                    ToastUtils.showLong("请录入化学品!");
+//                    return;
+//                }
+//                ActivityUtils.startActivity(UnlockActivity.class);
             }
         });
     }

+ 2 - 0
app/src/main/java/com/example/chemical/ui/plan/PlanAddActivityHelp.java

@@ -66,7 +66,9 @@ public class PlanAddActivityHelp {
                     if (hxpCabinetListVo.getCode() == 200) {
                         HxpLabCabinetVo hxpLabCabinetVo = hxpCabinetListVo.getData();
                         try {
+                            // 实验室负责人
                             RoomTool.getInstance().hxpLabDirectorDAO().insertAll(hxpLabCabinetVo.getAdminList());
+                            // 实验室安全员
                             RoomTool.getInstance().hxpLabSafeDAO().insertAll(hxpLabCabinetVo.getSafeList());
                         } catch (Exception e) {
                             LogUtils.e(Log.getStackTraceString(e));

+ 11 - 0
app/src/main/java/com/example/chemical/ui/plan/room/bean/locker/HxpCabinetAdminVo.java

@@ -12,6 +12,16 @@ public class HxpCabinetAdminVo {
 
     // ("实验室id")
     private long subId;
+    // 用户手机号
+    private String phone;
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
 
     public long getDoorId() {
         return doorId;
@@ -52,6 +62,7 @@ public class HxpCabinetAdminVo {
                 ", userId=" + userId +
                 ", userName='" + userName + '\'' +
                 ", subId=" + subId +
+                ", phone='" + phone + '\'' +
                 '}';
     }
 }

+ 15 - 0
app/src/main/java/com/example/chemical/ui/plan/room/bean/locker/HxpCabinetLockVo.java

@@ -19,6 +19,21 @@ public class HxpCabinetLockVo {
     // ("柜格编号:1,2,3 参考数据")
     private String cabinetLattice;
 
+    /**
+     * 0: 未开过锁
+     * 1: 开锁并开锁成功
+     * 2: 开过锁但开锁失败
+     * 3: 开锁中
+     */
+    private int unlockType = 0;
+
+    public int getUnlockType() {
+        return unlockType;
+    }
+
+    public void setUnlockType(int unlockType) {
+        this.unlockType = unlockType;
+    }
 
     public long getDoorId() {
         return doorId;

+ 2 - 2
app/src/main/java/com/example/chemical/ui/plan/room/dao/HxpDoorDAO.java

@@ -24,8 +24,8 @@ public interface HxpDoorDAO {
     @Query("SELECT * FROM hxpcabinetdoorvo")
     List<HxpCabinetDoorVo> getAll();
 
-    @Query("SELECT * FROM hxpcabinetdoorvo WHERE doorId = :dooId")
-    HxpCabinetDoorVo getById(long dooId);
+    @Query("SELECT * FROM hxpcabinetdoorvo WHERE doorId = :doorId")
+    HxpCabinetDoorVo getById(long doorId);
 
     @Query("SELECT * FROM hxpcabinetdoorvo WHERE cabinetId = :cabinetId")
     List<HxpCabinetDoorVo> getByCabinetId(long cabinetId);

+ 111 - 0
app/src/main/java/com/example/chemical/ui/plan/save_list/SaveListActivity.java

@@ -0,0 +1,111 @@
+package com.example.chemical.ui.plan.save_list;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.example.chemical.databinding.ActivitySaveListBinding;
+import com.example.chemical.ui.common.BaseCountDownActivity;
+import com.example.chemical.ui.plan.room.RoomTool;
+import com.example.chemical.ui.plan.room.bean.input_add.InventoryItemBean;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetDoorVo;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetLockVo;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetVo;
+import com.example.chemical.ui.plan.room.dao.HxpCabinetDAO;
+import com.example.chemical.ui.plan.room.dao.HxpDoorDAO;
+import com.example.chemical.ui.plan.room.dao.HxpInventoryDAO;
+import com.rc.httpcore.bean.AddChemicalBean;
+import com.rc.httpcore.bean.StockModel;
+import com.rc.httpcore.client.ApiRepository;
+import com.rc.httpcore.client.HttpTool;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import okhttp3.Response;
+
+public class SaveListActivity extends BaseCountDownActivity<ActivitySaveListBinding> {
+
+    protected ActivitySaveListBinding binding;
+    private SaveListAdapter saveListAdapter;
+    private Map<Long, List<SaveListBean>> saveListBeanMap = new TreeMap<>();
+
+    @NonNull
+    @Override
+    protected ActivitySaveListBinding createViewBinding() {
+        binding = ActivitySaveListBinding.inflate(getLayoutInflater());
+        return binding;
+    }
+
+    @Override
+    protected void initViews(@Nullable Bundle savedInstanceState) {
+        super.initViews(savedInstanceState);
+    }
+
+    @Override
+    protected void initData() {
+        super.initData();
+
+        HxpInventoryDAO inventoryDAO = RoomTool.getInstance().hxpInventoryDAO();
+        HxpCabinetDAO cabinetDAO = RoomTool.getInstance().hxpCabinetDAO();
+        HxpDoorDAO doorDAO = RoomTool.getInstance().hxpDoorDAO();
+        List<InventoryItemBean> inventoryItemBeanList = inventoryDAO.getAll();
+
+        for (int i = 0; i < inventoryItemBeanList.size(); i++) {
+            InventoryItemBean inventoryItemBean = inventoryItemBeanList.get(i);
+            SaveListBean saveListBean = new SaveListBean();
+            HxpCabinetVo cabinetVo = cabinetDAO.getById(inventoryItemBean.getCabinetId());
+            saveListBean.setCabinetId(cabinetVo.getCabinetId());
+            saveListBean.setCabinetName(cabinetVo.getCabinetName());
+            HxpCabinetDoorVo doorVo = doorDAO.getById(inventoryItemBean.getDoorId());
+            saveListBean.setDoorId(doorVo.getDoorId());
+            saveListBean.setDoorName(doorVo.getDoorName());
+            saveListBean.setInventoryItemBean(inventoryItemBean);
+
+            List<SaveListBean> saveListBeanList = saveListBeanMap.get(doorVo.getDoorId());
+            if (null == saveListBeanList) {
+                saveListBeanList = new ArrayList<>();
+                saveListBean.setTitle(true);
+            } else {
+                saveListBean.setTitle(false);
+            }
+
+
+            saveListBeanList.add(saveListBean);
+            saveListBeanMap.put(doorVo.getDoorId(), saveListBeanList);
+        }
+
+        saveListAdapter = new SaveListAdapter(this, saveListBeanMap);
+        binding.saveLV.setAdapter(saveListAdapter);
+//        List<String> logs = splitStringIntoChunks(GsonUtils.toJson(saveListBeanMap), 500);
+//        for (int i = 0; i < logs.size(); i++) {
+//            Log.d("Jacye", logs.get(i));
+//        }
+    }
+
+    public List<String> splitStringIntoChunks(String inputString, int chunkSize) {
+        if (chunkSize <= 0) {
+            throw new IllegalArgumentException("Chunk size must be greater than 0");
+        }
+
+        List<String> result = new ArrayList<>();
+        int startIndex = 0;
+
+        while (startIndex < inputString.length()) {
+            int endIndex = Math.min(startIndex + chunkSize, inputString.length());
+            String chunk = inputString.substring(startIndex, endIndex);
+            result.add(chunk);
+            startIndex = endIndex;
+        }
+
+        return result;
+    }
+}

+ 140 - 0
app/src/main/java/com/example/chemical/ui/plan/save_list/SaveListAdapter.java

@@ -0,0 +1,140 @@
+package com.example.chemical.ui.plan.save_list;
+
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import com.blankj.utilcode.util.LogUtils;
+import com.example.chemical.databinding.ItemSaveListBinding;
+import com.example.chemical.ui.plan.room.RoomTool;
+import com.example.chemical.ui.plan.room.bean.input_add.InventoryItemBean;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetDoorVo;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetLockVo;
+import com.example.chemical.ui.plan.room.dao.HxpDoorDAO;
+import com.rc.httpcore.bean.BelongingPersonBean;
+import com.rc.httpcore.bean.HxpChemicalVo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class SaveListAdapter extends BaseAdapter {
+
+    private SaveListActivity activity;
+    private List<SaveListBean> saveListBeanList = new ArrayList<>();
+    private HxpDoorDAO doorDAO;
+
+    public SaveListAdapter(SaveListActivity activity, Map<Long, List<SaveListBean>> saveListBeanMap) {
+        Set<Long> keySet = saveListBeanMap.keySet();
+        for (Long doorId : keySet) {
+            saveListBeanList.addAll(saveListBeanMap.get(doorId));
+        }
+        this.activity = activity;
+        doorDAO = RoomTool.getInstance().hxpDoorDAO();
+    }
+
+    @Override
+    public int getCount() {
+        return saveListBeanList.size();
+    }
+
+    @Override
+    public SaveListBean getItem(int position) {
+        return saveListBeanList.get(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return 0;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        SaveListBean saveListBean = getItem(position);
+        ViewHolder viewHolder;
+        if (convertView == null) {
+            ItemSaveListBinding binding = ItemSaveListBinding.inflate(LayoutInflater.from(activity), parent, false);
+            convertView = binding.getRoot();
+            viewHolder = new ViewHolder(binding);
+            convertView.setTag(viewHolder);
+            //  开门
+            binding.unlockBT.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    HxpCabinetDoorVo doorVo = doorDAO.getById(saveListBean.getDoorId());
+                    List<HxpCabinetLockVo> lockVoList = doorVo.getCabinetLockVoList();
+                    if (null != lockVoList && !lockVoList.isEmpty()) {
+                        HxpCabinetLockVo lockVo = lockVoList.get(0);
+                        LogUtils.d("再次开锁", lockVo.getLockName(), lockVo.getLockNum());
+                    }
+                }
+            });
+        } else {
+            viewHolder = (ViewHolder) convertView.getTag();
+        }
+
+        if (null != saveListBean) {
+            if (saveListBean.isTitle()) {
+                viewHolder.binding.titlleLL.setVisibility(View.VISIBLE);
+            } else {
+                viewHolder.binding.titlleLL.setVisibility(View.GONE);
+            }
+            String cabinetName = saveListBean.getCabinetName();
+            cabinetName = TextUtils.isEmpty(cabinetName) ? "" : cabinetName;
+            String doorName = saveListBean.getDoorName();
+            doorName = TextUtils.isEmpty(doorName) ? "" : doorName;
+            viewHolder.binding.cabinetTV.setText(cabinetName + "-" + doorName);
+
+            InventoryItemBean inventoryItemBean = saveListBean.getInventoryItemBean();
+            if (null != inventoryItemBean) {
+                HxpChemicalVo chemicalVo = inventoryItemBean.getHxpChemicalVo();
+                if (null != chemicalVo) {
+                    // 化学品名称
+                    String chemicalName = chemicalVo.getChemicalName();
+                    viewHolder.binding.name.setText(TextUtils.isEmpty(chemicalName) ? "" : cabinetName);
+                    // CAS
+                    String casNum = chemicalVo.getCasNum();
+                    viewHolder.binding.cas.setText(TextUtils.isEmpty(casNum) ? "" : casNum);
+                    // 类别
+                    String chemicalCategoryName = chemicalVo.getChemicalCategoryName();
+                    viewHolder.binding.type.setText(TextUtils.isEmpty(chemicalCategoryName) ? "" : chemicalCategoryName);
+                    // 级别
+                    String levelName = chemicalVo.getChemicalLevelName();
+                    viewHolder.binding.level.setText(TextUtils.isEmpty(levelName) ? "" : levelName);
+                    // 规格
+                    double specNum = chemicalVo.getSpecNum();
+                    String specUnit = chemicalVo.getSpecUnit();
+                    specUnit = TextUtils.isEmpty(specUnit) ? "" : specUnit;
+                    viewHolder.binding.specs.setText(specNum + "/" + specUnit);
+                    // 净含量
+                    double netWt = chemicalVo.getNetWt();
+                    viewHolder.binding.netWT.setText(netWt + specUnit);
+                    // 数量
+                    int size = inventoryItemBean.getSize();
+                    viewHolder.binding.hxpSum.setText(String.valueOf(size));
+                    // 归属人
+                    BelongingPersonBean personBean = inventoryItemBean.getBelongingPersonBean();
+                    String userName = personBean.getUserName();
+                    viewHolder.binding.owner.setText(TextUtils.isEmpty(userName) ? "" : userName);
+                    // 是否全新
+//                    viewHolder.binding.cas.setText(TextUtils.isEmpty(casNum) ? "" : casNum);
+                    // 存储层
+                    int layer = inventoryItemBean.getLayer();
+                    viewHolder.binding.layer.setText(layer + "层");
+                }
+            }
+        }
+        return convertView;
+    }
+
+    static class ViewHolder {
+        ItemSaveListBinding binding;
+
+        public ViewHolder(ItemSaveListBinding binding) {
+            this.binding = binding;
+        }
+    }
+}

+ 61 - 0
app/src/main/java/com/example/chemical/ui/plan/save_list/SaveListBean.java

@@ -0,0 +1,61 @@
+package com.example.chemical.ui.plan.save_list;
+
+import com.example.chemical.ui.plan.room.bean.input_add.InventoryItemBean;
+
+public class SaveListBean {
+    private long cabinetId;
+    private String cabinetName;
+    private long doorId;
+    private String doorName;
+    private boolean isTitle;
+
+    private InventoryItemBean inventoryItemBean;
+
+    public long getCabinetId() {
+        return cabinetId;
+    }
+
+    public void setCabinetId(long cabinetId) {
+        this.cabinetId = cabinetId;
+    }
+
+    public String getCabinetName() {
+        return cabinetName;
+    }
+
+    public void setCabinetName(String cabinetName) {
+        this.cabinetName = cabinetName;
+    }
+
+    public long getDoorId() {
+        return doorId;
+    }
+
+    public void setDoorId(long doorId) {
+        this.doorId = doorId;
+    }
+
+    public String getDoorName() {
+        return doorName;
+    }
+
+    public void setDoorName(String doorName) {
+        this.doorName = doorName;
+    }
+
+    public boolean isTitle() {
+        return isTitle;
+    }
+
+    public void setTitle(boolean title) {
+        isTitle = title;
+    }
+
+    public InventoryItemBean getInventoryItemBean() {
+        return inventoryItemBean;
+    }
+
+    public void setInventoryItemBean(InventoryItemBean inventoryItemBean) {
+        this.inventoryItemBean = inventoryItemBean;
+    }
+}

+ 214 - 27
app/src/main/java/com/example/chemical/ui/plan/unlock/UnlockActivity.java

@@ -1,40 +1,62 @@
 package com.example.chemical.ui.plan.unlock;
 
+import android.content.Intent;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.view.View;
 
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.blankj.utilcode.util.ActivityUtils;
-import com.example.chemical.ChemicalApp;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.ThreadUtils;
 import com.example.chemical.databinding.ActivityUnlockBinding;
 import com.example.chemical.ui.common.BaseCountDownActivity;
 import com.example.chemical.ui.plan.room.RoomTool;
 import com.example.chemical.ui.plan.room.bean.input_add.InventoryItemBean;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetAdminVo;
 import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetDoorVo;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetLockVo;
 import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetVo;
+import com.example.chemical.ui.plan.room.bean.locker.HxpLabDirector;
+import com.example.chemical.ui.plan.room.bean.locker.HxpLabSafe;
 import com.example.chemical.ui.plan.room.dao.HxpCabinetDAO;
 import com.example.chemical.ui.plan.room.dao.HxpDoorDAO;
 import com.example.chemical.ui.plan.room.dao.HxpInventoryDAO;
+import com.example.chemical.ui.plan.save_list.SaveListActivity;
 import com.example.chemical.ui.verify.DoubleVerifyActivity;
-import com.example.chemical.ui.verify.TwoVerificationActivity;
-import com.example.chemical.utils.UiManager;
 import com.rc.httpcore.bean.HxpChemicalVo;
+import com.rc.httpcore.bean.UserValidationBean;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
+import java.util.Set;
+
+import kotlin.Triple;
 
 public class UnlockActivity extends BaseCountDownActivity<ActivityUnlockBinding> {
-    private ActivityUnlockBinding binding;
+    protected ActivityUnlockBinding binding;
+    private ActivityResultLauncher<Intent> intentActivityResultLauncher;
+    protected UnlockActivityHelp activityHelp;
 
+    // 验证通过的人员1
+    private UserValidationBean userValidation1;
+    // 验证通过的人员2
+    private UserValidationBean userValidation2;
     /**
      * 是否管控
      */
     private boolean isControl = false;
 
+    protected UnlockAdapter unlockAdapter;
+
     @NonNull
     @Override
     protected ActivityUnlockBinding createViewBinding() {
@@ -44,7 +66,7 @@ public class UnlockActivity extends BaseCountDownActivity<ActivityUnlockBinding>
     @Override
     protected void initViews(@Nullable Bundle savedInstanceState) {
         super.initViews(savedInstanceState);
-
+        activityHelp = new UnlockActivityHelp(this);
         HxpCabinetDAO hxpCabinetDAO = RoomTool.getInstance().hxpCabinetDAO();
         HxpDoorDAO hxpDoorDAO = RoomTool.getInstance().hxpDoorDAO();
         HxpInventoryDAO hxpInventoryDAO = RoomTool.getInstance().hxpInventoryDAO();
@@ -59,34 +81,199 @@ public class UnlockActivity extends BaseCountDownActivity<ActivityUnlockBinding>
             }
         }
 
-
         // 柜门信息理应需要从柜门表中遍历塞到柜子中  观察数据是否异常
         List<HxpCabinetVo> cabinetList = hxpCabinetDAO.getAll();
         List<HxpCabinetDoorVo> doorList = hxpDoorDAO.getAll();
+        List<HxpCabinetLockVo> unlockList = new ArrayList<>();
+        Iterator<HxpCabinetDoorVo> doorVoIterator = doorList.iterator();
+
+        // 删除不需要开门的柜门
+        while (doorVoIterator.hasNext()) {
+            HxpCabinetDoorVo doorVo = doorVoIterator.next();
+            List<HxpCabinetLockVo> lockVoList = doorVo.getCabinetLockVoList();
+            if (null == lockVoList || lockVoList.isEmpty()) {
+                doorVoIterator.remove();
+            } else {
+                Iterator<HxpCabinetLockVo> lockVoIterator = lockVoList.iterator();
+                while (lockVoIterator.hasNext()) {
+                    HxpCabinetLockVo lockVo = lockVoIterator.next();
+                    if (lockVo.getUnlockingMethod() != 2) {
+                        lockVoIterator.remove();
+                    }
+                }
+                if (lockVoList.isEmpty()) {
+                    doorVoIterator.remove();
+                }
+            }
+        }
+        // 删除不需要开门的柜子
+        Iterator<HxpCabinetVo> cabinetVoIterator = cabinetList.iterator();
+        while (cabinetVoIterator.hasNext()) {
+            HxpCabinetVo cabinetVo = cabinetVoIterator.next();
+            boolean hasDoor = false;
+            for (int i = 0; i < doorList.size(); i++) {
+                HxpCabinetDoorVo doorVo = doorList.get(i);
+                if (cabinetVo.getCabinetId() == doorVo.getCabinetId()) {
+                    hasDoor = true;
+                    break;
+                }
+            }
+            if (!hasDoor) {
+                cabinetVoIterator.remove();
+            }
+        }
+
+        // 需要开锁的柜子
+        for (int i = 0; i < doorList.size(); i++) {
+            HxpCabinetDoorVo doorVo = doorList.get(i);
+            List<HxpCabinetLockVo> lockVoList = doorVo.getCabinetLockVoList();
+            unlockList.addAll(lockVoList);
+        }
+
+        if (!unlockList.isEmpty()) {
+            ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Triple<String, String, String>>() {
+                @Override
+                public Triple<String, String, String> doInBackground() throws Throwable {
+                    // 公共柜门管理员
+                    String adminInfo = "", directorInfo = "", safeInfo = "";
+                    List<HxpCabinetAdminVo> cabinetAdminVoList = getCommonAdminUserIds(doorList);
+                    if (null != cabinetAdminVoList && !cabinetAdminVoList.isEmpty()) {
+                        HxpCabinetAdminVo admin = cabinetAdminVoList.get(0);
+                        adminInfo = admin.getUserName() + " " + admin.getPhone();
+                        if (cabinetAdminVoList.size() > 1) {
+                            StringBuilder sb = new StringBuilder();
+                            for (int i = 0; i < cabinetAdminVoList.size(); i++) {
+                                HxpCabinetAdminVo adminVo = cabinetAdminVoList.get(i);
+                                sb.append(adminVo.getUserName());
+                                sb.append(" ");
+                                sb.append(adminVo.getPhone());
+                                sb.append("\n");
+                            }
+                            sb.delete(sb.length() - 1, sb.length());
+                            adminInfo = sb.toString();
+                        }
+                    }
+                    // 实验室负责人
+                    List<HxpLabDirector> hxpLabDirectorList = RoomTool.getInstance().hxpLabDirectorDAO().getAll();
+                    if (null != hxpLabDirectorList && !hxpLabDirectorList.isEmpty()) {
+                        HxpLabDirector labDirector = hxpLabDirectorList.get(0);
+                        directorInfo = labDirector.getUserName() + " " + labDirector.getMobile();
+                        if (hxpLabDirectorList.size() > 1) {
+                            StringBuilder sb = new StringBuilder();
+                            for (int i = 0; i < hxpLabDirectorList.size(); i++) {
+                                HxpLabDirector director = hxpLabDirectorList.get(i);
+                                sb.append(director.getUserName());
+                                sb.append(" ");
+                                sb.append(director.getMobile());
+                                sb.append("\n");
+                            }
+                            sb.delete(sb.length() - 1, sb.length());
+                            directorInfo = sb.toString();
+                        }
+                    }
+                    // 实验室安全员
+                    List<HxpLabSafe> labSafeList = RoomTool.getInstance().hxpLabSafeDAO().getAll();
+                    if (null != labSafeList && !labSafeList.isEmpty()) {
+                        HxpLabSafe safe = labSafeList.get(0);
+                        safeInfo = safe.getUserName() + " " + safe.getMobile();
+                        if (labSafeList.size() > 1) {
+                            StringBuilder sb = new StringBuilder();
+                            for (int i = 0; i < labSafeList.size(); i++) {
+                                HxpLabSafe labSafe = labSafeList.get(i);
+                                sb.append(labSafe.getUserName());
+                                sb.append(" ");
+                                sb.append(labSafe.getMobile());
+                                sb.append("\n");
+                            }
+                            sb.delete(sb.length() - 1, sb.length());
+                            safeInfo = sb.toString();
+                        }
+                    }
+
+                    return new Triple<>(adminInfo, directorInfo, safeInfo);
+                }
+
+                @Override
+                public void onSuccess(Triple<String, String, String> result) {
+                    String adminInfo = result.getFirst();
+                    String directorInfo = result.getSecond();
+                    String safeInfo = result.getThird();
+                    binding.adminTV.setText(TextUtils.isEmpty(adminInfo) ? "" : adminInfo);
+                    binding.directorTV.setText(TextUtils.isEmpty(directorInfo) ? "" : directorInfo);
+                    binding.safetyTV.setText(TextUtils.isEmpty(safeInfo) ? "" : safeInfo);
+                }
+            });
+        }
 
         binding.cabinetTV.setText(cabinetList.size() + "个");
         binding.doorTV.setText(doorList.size() + "个");
-        binding.unlockGV.setAdapter(new UnlockAdapter(this, cabinetList, doorList));
-
-        binding.cancelBT.setOnClickListener(v -> finish());
-
-        binding.validateBT.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                ActivityUtils.startActivity(DoubleVerifyActivity.class);
-//                /* 以下前同事代码*/
-//                String verifyType = ChemicalApp.confs.getVerifyType();
-//                Map<String, Object> map = new HashMap<>();
-//                map.put("chemicalLevel", isControl ? 1 : 2);
-//                map.put("mTag", 0);
-//                if (verifyType.length() == 1) {
-////                    map.put("dooId",)
-//                    map.put("mVerTyps", Integer.valueOf(verifyType));
-//                    UiManager.INSTANCE.switcher(UnlockActivity.this, map, TwoVerificationActivity.class);
-//                } else {
-//                    map.put("mVerTyps", 0);
-//                }
+        unlockAdapter = new UnlockAdapter(this, cabinetList, doorList);
+        binding.unlockGV.setAdapter(unlockAdapter);
+
+        intentActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
+            if (result.getResultCode() == RESULT_OK) {
+                Intent intent = result.getData();
+                if (null != intent && intent.hasExtra("userValidation1") && intent.hasExtra("userValidation2")) {
+                    userValidation1 = GsonUtils.fromJson(intent.getStringExtra("userValidation1"), UserValidationBean.class);
+                    userValidation2 = GsonUtils.fromJson(intent.getStringExtra("userValidation2"), UserValidationBean.class);
+                }
+                binding.validateBT.setVisibility(View.GONE);
+                binding.unlockLL.setVisibility(View.VISIBLE);
+                activityHelp.unlock(unlockList, cabinetList, doorList, userValidation1, userValidation2);
             }
         });
+        binding.validateBT.setOnClickListener(v -> intentActivityResultLauncher.launch(new Intent(UnlockActivity.this, DoubleVerifyActivity.class)));
+
+        binding.retryBT.setOnClickListener(v -> activityHelp.reTryUnlock());
+        binding.saveBT.setOnClickListener(v -> ActivityUtils.startActivity(SaveListActivity.class));
     }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        intentActivityResultLauncher.unregister();
+    }
+
+    public static List<HxpCabinetAdminVo> getCommonAdminUserIds(List<HxpCabinetDoorVo> doorList) {
+        if (doorList == null || doorList.isEmpty()) {
+            return null;
+        }
+        Map<Long, Integer> userIdCount = new HashMap<Long, Integer>();
+        int totalDoors = doorList.size();
+
+        // 统计所有 UserId 的出现次数
+        for (HxpCabinetDoorVo door : doorList) {
+            Set<Long> uniqueIds = new HashSet<Long>(); // 记录当前 Door 的唯一 UserId,防止重复计数
+            for (HxpCabinetAdminVo admin : door.getCabinetAdminVoList()) {
+                uniqueIds.add(admin.getUserId());
+            }
+            for (Long userId : uniqueIds) {
+                userIdCount.put(userId, userIdCount.containsKey(userId) ? userIdCount.get(userId) + 1 : 1);
+            }
+        }
+
+        // 过滤出出现在所有 Door 中的 UserId
+        Set<Long> commonUserIds = new HashSet<Long>();
+        for (Map.Entry<Long, Integer> entry : userIdCount.entrySet()) {
+            if (entry.getValue() == totalDoors) {
+                commonUserIds.add(entry.getKey());
+            }
+        }
+
+        if (commonUserIds.isEmpty()) {
+            return null;
+        }
+
+        // 仅返回第一个 Door 中符合的 Admin
+        List<HxpCabinetAdminVo> commonAdmins = new ArrayList<HxpCabinetAdminVo>();
+        for (HxpCabinetAdminVo admin : doorList.get(0).getCabinetAdminVoList()) {
+            if (commonUserIds.contains(admin.getUserId())) {
+                commonAdmins.add(admin);
+            }
+        }
+
+        return commonAdmins;
+    }
+
+
 }

+ 299 - 0
app/src/main/java/com/example/chemical/ui/plan/unlock/UnlockActivityHelp.java

@@ -0,0 +1,299 @@
+package com.example.chemical.ui.plan.unlock;
+
+import android.os.CountDownTimer;
+import android.util.Log;
+import android.view.View;
+
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.example.chemical.ChemicalApp;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetDoorVo;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetLockVo;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetVo;
+import com.rc.httpcore.bean.UserValidationBean;
+import com.rc.httpcore.client.ApiRepository;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import io.reactivex.disposables.Disposable;
+
+public class UnlockActivityHelp {
+    private UnlockActivity activity;
+    Map<String, Object> map = new HashMap<>();
+    // 柜子集合
+    List<HxpCabinetVo> cabinetList;
+    // 柜门集合
+    List<HxpCabinetDoorVo> doorList;
+    // 智能锁集合
+    List<HxpCabinetLockVo> unlockList;
+
+    private UserValidationBean userValidation1, userValidation2;
+    private Disposable unlockDisposable, unlockStatusDisposable;
+
+    /**
+     * 当前正在操作的锁
+     */
+    private HxpCabinetLockVo nowLockVo;
+
+
+    public UnlockActivityHelp(UnlockActivity activity) {
+        this.activity = activity;
+    }
+
+
+    public void reTryUnlock() {
+        for (int i = 0; i < unlockList.size(); i++) {
+            HxpCabinetLockVo hxpCabinetLockVo = unlockList.get(i);
+            if (2 == hxpCabinetLockVo.getUnlockType()) {
+                hxpCabinetLockVo.setUnlockType(0);
+            }
+        }
+        nowLockVo = getNextNotUnlocked();
+        unlockTask();
+    }
+
+    /**
+     * 批量开锁
+     *
+     * @param cabinetList     柜子集合
+     * @param doorList        柜门集合
+     * @param userValidation1 验证通过的人1
+     * @param userValidation2 验证通过的人2
+     */
+    public void unlock(List<HxpCabinetLockVo> unlockList, List<HxpCabinetVo> cabinetList, List<HxpCabinetDoorVo> doorList, UserValidationBean userValidation1, UserValidationBean userValidation2) {
+        this.unlockList = unlockList;
+        this.userValidation1 = userValidation1;
+        this.userValidation2 = userValidation2;
+        this.cabinetList = cabinetList;
+        this.doorList = doorList;
+        unlockTask();
+    }
+
+    public void unlock(HxpCabinetLockVo nowLockVo) {
+        activity.showLoading("开锁中...", false);
+        map.clear();
+        map.put("subId", ChemicalApp.subjectId);
+        map.put("subName", ChemicalApp.subjectName);
+
+        HxpCabinetVo hxpCabinetVo = findCabinetByDoorId(nowLockVo.getDoorId());
+        if (null != hxpCabinetVo) {
+            map.put("cabinetId", hxpCabinetVo.getCabinetId());
+            map.put("cabinetName", hxpCabinetVo.getCabinetName());
+        }
+
+        HxpCabinetDoorVo hxpDoorVo = findDoorByDoorId(nowLockVo.getDoorId());
+        if (null != hxpDoorVo) {
+            map.put("doorId", hxpDoorVo.getDoorId());
+            map.put("doorName", hxpDoorVo.getDoorName());
+        }
+        List<String> lockNumList = new ArrayList<>();
+        lockNumList.add(nowLockVo.getLockNum());
+        map.put("lockNumList", lockNumList);
+        map.put("operationType", 3);
+        if (ChemicalApp.subRoom != null) {
+            map.put("subRoom", ChemicalApp.subRoom);
+        }
+        map.put("type", true);
+        if (userValidation1 != null) {
+            map.put("oneUserId", userValidation1.getUserId());
+            map.put("oneUserName", userValidation1.getUserName());
+        }
+        if (null != userValidation2) {
+            map.put("twoUserId", userValidation2.getUserId());
+            map.put("twoUserName", userValidation2.getUserName());
+        }
+        LogUtils.json("手动开锁", map);
+        unlockDisposable = ApiRepository.INSTANCE.lockOperate(map).subscribe(aBoolean -> {
+            LogUtils.d("手动开锁", nowLockVo.getLockNum(), nowLockVo.getLockName(), aBoolean);
+            if (aBoolean) {
+                nowLockVo.setUnlockType(3);
+                activity.unlockAdapter.notifyDataSetChanged();
+
+                unlockStatusCDTimer = new CountDownTimer(6 * 1000, 3000) {
+                    @Override
+                    public void onTick(long millisUntilFinished) {
+                        if (null != unlockStatusDisposable) {
+                            activity.delDisposable(unlockStatusDisposable);
+                        }
+                        unlockStatusDisposable = ApiRepository.INSTANCE.getLocks(ChemicalApp.subjectId, nowLockVo.getLockNum()).subscribe(aBoolean -> {
+                            LogUtils.d("手动开锁查询状态", nowLockVo.getLockNum(), nowLockVo.getLockName(), aBoolean);
+                            if (aBoolean) {
+                                unlockStatusCDTimer.cancel();
+                                ToastUtils.showLong("开门成功!");
+                                nowLockVo.setUnlockType(1);
+                                activity.unlockAdapter.notifyDataSetChanged();
+                                activity.dismissLoading();
+                            }
+                        }, throwable -> LogUtils.e(Log.getStackTraceString(throwable)));
+                        activity.addDisposable(unlockStatusDisposable);
+                    }
+
+                    @Override
+                    public void onFinish() {
+                        LogUtils.d("手动开锁失败", nowLockVo.getLockNum(), nowLockVo.getLockName());
+                        activity.unlockAdapter.notifyDataSetChanged();
+                        activity.dismissLoading();
+                        nowLockVo.setUnlockType(2);
+                        HxpCabinetDoorVo doorVo = findDoorByLockId(nowLockVo.getLockId());
+                        if (null != doorVo) {
+                            ToastUtils.showLong(doorVo.getDoorName() + "开门失败!");
+                        } else {
+                            ToastUtils.showLong("开门失败!");
+                        }
+                    }
+                };
+                unlockStatusCDTimer.start();
+            } else {
+                nowLockVo.setUnlockType(2);
+            }
+        }, throwable -> LogUtils.e(Log.getStackTraceString(throwable)));
+        activity.addDisposable(unlockDisposable);
+    }
+
+    private HxpCabinetDoorVo findDoorByLockId(long lockId) {
+        for (int i = 0; i < doorList.size(); i++) {
+            HxpCabinetDoorVo doorVo = doorList.get(i);
+            List<HxpCabinetLockVo> lockVoList = doorVo.getCabinetLockVoList();
+            if (null != lockVoList) {
+                for (int j = 0; j < lockVoList.size(); j++) {
+                    HxpCabinetLockVo lockVo = lockVoList.get(j);
+                    if (lockVo.getLockId() == lockId) {
+                        return doorVo;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 获取一个没有执行过开锁操作的锁
+     */
+    private HxpCabinetLockVo getNextNotUnlocked() {
+        for (int i = 0; i < unlockList.size(); i++) {
+            HxpCabinetLockVo hxpCabinetLockVo = unlockList.get(i);
+            // 获取一个未开过锁的锁
+            if (0 == hxpCabinetLockVo.getUnlockType()) {
+                return hxpCabinetLockVo;
+            }
+        }
+        return null;
+    }
+
+    private HxpCabinetVo findCabinetByDoorId(long doorId) {
+        HxpCabinetDoorVo hxpCabinetDoorVo = findDoorByDoorId(doorId);
+        if (null != hxpCabinetDoorVo) {
+            for (int j = 0; j < cabinetList.size(); j++) {
+                HxpCabinetVo cabinetVo = cabinetList.get(j);
+                if (hxpCabinetDoorVo.getCabinetId() == cabinetVo.getCabinetId()) {
+                    return cabinetVo;
+                }
+            }
+        }
+        return null;
+    }
+
+    private HxpCabinetDoorVo findDoorByDoorId(long doorId) {
+        for (int i = 0; i < doorList.size(); i++) {
+            HxpCabinetDoorVo hxpCabinetDoorVo = doorList.get(i);
+            if (doorId == hxpCabinetDoorVo.getDoorId()) {
+                return hxpCabinetDoorVo;
+            }
+        }
+        return null;
+    }
+
+    private CountDownTimer unlockStatusCDTimer;
+
+    private void unlockTask() {
+        if (null != unlockStatusCDTimer) {
+            unlockStatusCDTimer.cancel();
+            unlockStatusCDTimer = null;
+        }
+        if (null != unlockDisposable) {
+            activity.delDisposable(unlockDisposable);
+        }
+        nowLockVo = getNextNotUnlocked();
+        activity.unlockAdapter.notifyDataSetChanged();
+
+        if (null == nowLockVo) {
+            activity.binding.retryBT.setVisibility(View.VISIBLE);
+            activity.binding.saveBT.setVisibility(View.VISIBLE);
+            activity.binding.unlockLL.setVisibility(View.GONE);
+            activity.unlockAdapter.setUnlockEnd(true);
+            activity.unlockAdapter.notifyDataSetChanged();
+            LogUtils.json("开锁结束", unlockList);
+            return;
+        }
+
+        map.clear();
+        map.put("subId", ChemicalApp.subjectId);
+        map.put("subName", ChemicalApp.subjectName);
+
+        HxpCabinetVo hxpCabinetVo = findCabinetByDoorId(nowLockVo.getDoorId());
+        if (null != hxpCabinetVo) {
+            map.put("cabinetId", hxpCabinetVo.getCabinetId());
+            map.put("cabinetName", hxpCabinetVo.getCabinetName());
+        }
+
+        HxpCabinetDoorVo hxpDoorVo = findDoorByDoorId(nowLockVo.getDoorId());
+        if (null != hxpDoorVo) {
+            map.put("doorId", hxpDoorVo.getDoorId());
+            map.put("doorName", hxpDoorVo.getDoorName());
+        }
+        List<String> lockNumList = new ArrayList<>();
+        lockNumList.add(nowLockVo.getLockNum());
+        map.put("lockNumList", lockNumList);
+        map.put("operationType", 3);
+        if (ChemicalApp.subRoom != null) {
+            map.put("subRoom", ChemicalApp.subRoom);
+        }
+        map.put("type", true);
+        if (userValidation1 != null) {
+            map.put("oneUserId", userValidation1.getUserId());
+            map.put("oneUserName", userValidation1.getUserName());
+        }
+        if (null != userValidation2) {
+            map.put("twoUserId", userValidation2.getUserId());
+            map.put("twoUserName", userValidation2.getUserName());
+        }
+
+        unlockDisposable = ApiRepository.INSTANCE.lockOperate(map).subscribe(aBoolean -> {
+            if (aBoolean) {
+                nowLockVo.setUnlockType(3);
+                activity.unlockAdapter.notifyDataSetChanged();
+
+                unlockStatusCDTimer = new CountDownTimer(6 * 1000, 3000) {
+                    @Override
+                    public void onTick(long millisUntilFinished) {
+                        if (null != unlockStatusDisposable) {
+                            activity.delDisposable(unlockStatusDisposable);
+                        }
+                        unlockStatusDisposable = ApiRepository.INSTANCE.getLocks(ChemicalApp.subjectId, nowLockVo.getLockNum()).subscribe(aBoolean -> {
+                            LogUtils.d("查询结果", nowLockVo.getLockNum(), nowLockVo.getLockName(), aBoolean);
+                            if (aBoolean) {
+                                nowLockVo.setUnlockType(1);
+                                unlockTask();
+                            }
+                        }, throwable -> LogUtils.e(Log.getStackTraceString(throwable)));
+                        activity.addDisposable(unlockStatusDisposable);
+                    }
+
+                    @Override
+                    public void onFinish() {
+                        nowLockVo.setUnlockType(2);
+                        unlockTask();
+                    }
+                };
+                unlockStatusCDTimer.start();
+            } else {
+                nowLockVo.setUnlockType(2);
+                unlockTask();
+            }
+        }, throwable -> LogUtils.e(Log.getStackTraceString(throwable)));
+        activity.addDisposable(unlockDisposable);
+    }
+}

+ 126 - 14
app/src/main/java/com/example/chemical/ui/plan/unlock/UnlockAdapter.java

@@ -1,6 +1,7 @@
 package com.example.chemical.ui.plan.unlock;
 
 import android.content.Context;
+import android.graphics.Color;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -9,26 +10,26 @@ import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 
 import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ToastUtils;
 import com.example.chemical.R;
 import com.example.chemical.databinding.ItemDoorUnlockBinding;
-import com.example.chemical.ui.plan.room.RoomTool;
 import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetDoorVo;
+import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetLockVo;
 import com.example.chemical.ui.plan.room.bean.locker.HxpCabinetVo;
-import com.example.chemical.ui.plan.room.dao.HxpDoorDAO;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class UnlockAdapter extends BaseAdapter {
     List<HxpCabinetVo> cabinetList;
     List<HxpCabinetDoorVo> doorList;
-    private Context context;
-    private HxpDoorDAO doorDAO;
+    private UnlockActivity activity;
+    private boolean isUnlockEnd = false;
 
-    public UnlockAdapter(Context context, List<HxpCabinetVo> cabinetList, List<HxpCabinetDoorVo> doorList) {
+    public UnlockAdapter(UnlockActivity activity, List<HxpCabinetVo> cabinetList, List<HxpCabinetDoorVo> doorList) {
         this.cabinetList = cabinetList;
         this.doorList = doorList;
-        this.context = context;
-        doorDAO = RoomTool.getInstance().hxpDoorDAO();
+        this.activity = activity;
     }
 
     @Override
@@ -48,9 +49,10 @@ public class UnlockAdapter extends BaseAdapter {
 
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
+        HxpCabinetVo cabinet = getItem(position);
         ViewHolder viewHolder;
         if (convertView == null) {
-            ItemDoorUnlockBinding binding = ItemDoorUnlockBinding.inflate(LayoutInflater.from(context), parent, false);
+            ItemDoorUnlockBinding binding = ItemDoorUnlockBinding.inflate(LayoutInflater.from(activity), parent, false);
             convertView = binding.getRoot();
             viewHolder = new ViewHolder(binding);
             convertView.setTag(viewHolder);
@@ -58,38 +60,132 @@ public class UnlockAdapter extends BaseAdapter {
             binding.upDoorUnlockBT.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
-
+                    if (null != cabinet) {
+                        List<HxpCabinetDoorVo> hxpCabinetDoorVoList = findDoorByCabinetId(cabinet.getCabinetId());
+                        if (!hxpCabinetDoorVoList.isEmpty()) {
+                            HxpCabinetDoorVo upDoor = hxpCabinetDoorVoList.get(0);
+                            List<HxpCabinetLockVo> lockVoList = upDoor.getCabinetLockVoList();
+                            if (null != lockVoList && !lockVoList.isEmpty()) {
+                                activity.activityHelp.unlock(lockVoList.get(0));
+                                return;
+                            }
+                        }
+                        ToastUtils.showLong("开锁失败!");
+                    }
                 }
             });
             // 下门
             binding.downDoorUnlockBT.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
-
+                    if (null != cabinet) {
+                        List<HxpCabinetDoorVo> hxpCabinetDoorVoList = findDoorByCabinetId(cabinet.getCabinetId());
+                        if (!hxpCabinetDoorVoList.isEmpty()) {
+                            HxpCabinetDoorVo downDoor = hxpCabinetDoorVoList.get(1);
+                            List<HxpCabinetLockVo> lockVoList = downDoor.getCabinetLockVoList();
+                            if (null != lockVoList && !lockVoList.isEmpty()) {
+                                activity.activityHelp.unlock(lockVoList.get(0));
+                                return;
+                            }
+                        }
+                        ToastUtils.showLong("开锁失败!");
+                    }
                 }
             });
         } else {
             viewHolder = (ViewHolder) convertView.getTag();
         }
 
-        HxpCabinetVo cabinet = getItem(position);
         if (null != cabinet) {
             try {
                 String cabinetName = cabinet.getCabinetName();
                 viewHolder.binding.cabinetNameTV.setText(TextUtils.isEmpty(cabinetName) ? "" : cabinetName);
 
-                List<HxpCabinetDoorVo> hxpCabinetDoorVoList = doorDAO.getByCabinetId(cabinet.getCabinetId());
+                List<HxpCabinetDoorVo> hxpCabinetDoorVoList = findDoorByCabinetId(cabinet.getCabinetId());
                 int doorNum = cabinet.getDoorNum();
                 HxpCabinetDoorVo upDoor = hxpCabinetDoorVoList.get(0);
                 String doorName = upDoor.getDoorName();
                 viewHolder.binding.upDoorNameTV.setText(TextUtils.isEmpty(doorName) ? "" : doorName);
+
                 if (doorNum > 1) {
+                    viewHolder.binding.cabinetLL.setBackgroundResource(R.mipmap.img_skm);
+                } else {
+                    viewHolder.binding.cabinetLL.setBackgroundResource(R.mipmap.img_dkm);
+                }
+
+                // 上柜门开锁
+                List<HxpCabinetLockVo> upLockList = upDoor.getCabinetLockVoList();
+                HxpCabinetLockVo upLock = upLockList.get(0);
+                switch (upLock.getUnlockType()) {
+                    // 0: 未开过锁
+                    case 0:
+                        viewHolder.binding.upDoorNameIV.setBackgroundResource(R.mipmap.icon_gzxx_zh);
+                        viewHolder.binding.upDoorNameTV.setTextColor(Color.parseColor("#FF333333"));
+                        viewHolder.binding.upDoorUnlockBT.setVisibility(View.GONE);
+                        break;
+                    // 1: 开锁并开锁成功
+                    case 1:
+                        viewHolder.binding.upDoorNameIV.setBackgroundResource(R.mipmap.icon_gzxx_xz);
+                        viewHolder.binding.upDoorNameTV.setTextColor(Color.parseColor("#FF0FB525"));
+                        viewHolder.binding.upDoorUnlockBT.setVisibility(View.GONE);
+                        break;
+                    // 2: 开过锁但开锁失败
+                    case 2:
+                        viewHolder.binding.upDoorNameIV.setBackgroundResource(R.mipmap.icon_gzxx_cw);
+                        viewHolder.binding.upDoorNameTV.setTextColor(Color.parseColor("#FFFF2E2E"));
+                        viewHolder.binding.upDoorUnlockBT.setVisibility(View.GONE);
+                        break;
+                    // 3: 开锁中
+                    case 3:
+                        viewHolder.binding.upDoorNameIV.setBackgroundResource(R.mipmap.icon_gzxx_zh);
+                        viewHolder.binding.upDoorNameTV.setTextColor(Color.parseColor("#FF333333"));
+                        viewHolder.binding.upDoorUnlockBT.setVisibility(View.GONE);
+                        break;
+                }
+                if (isUnlockEnd) {
+                    viewHolder.binding.upDoorUnlockBT.setVisibility(View.VISIBLE);
+                }
+
+                if (hxpCabinetDoorVoList.size() > 1) {
                     HxpCabinetDoorVo downDoor = hxpCabinetDoorVoList.get(1);
                     String downDoorName = downDoor.getDoorName();
                     viewHolder.binding.downDoorNameTV.setText(TextUtils.isEmpty(downDoorName) ? "" : downDoorName);
-                    viewHolder.binding.downDoorNameTV.setVisibility(View.VISIBLE);
-                    viewHolder.binding.cabinetLL.setBackgroundResource(R.mipmap.img_skm);
+                    viewHolder.binding.downDoorUnlockLL.setVisibility(View.VISIBLE);
+                    // 下柜们开锁
+                    List<HxpCabinetLockVo> downLockList = downDoor.getCabinetLockVoList();
+                    HxpCabinetLockVo downLock = downLockList.get(0);
+                    switch (downLock.getUnlockType()) {
+                        // 0: 未开过锁
+                        case 0:
+                            viewHolder.binding.downDoorNameIV.setBackgroundResource(R.mipmap.icon_gzxx_zh);
+                            viewHolder.binding.downDoorNameTV.setTextColor(Color.parseColor("#FF333333"));
+                            viewHolder.binding.downDoorUnlockBT.setVisibility(View.GONE);
+                            break;
+                        // 1: 开锁并开锁成功
+                        case 1:
+                            viewHolder.binding.downDoorNameIV.setBackgroundResource(R.mipmap.icon_gzxx_xz);
+                            viewHolder.binding.downDoorNameTV.setTextColor(Color.parseColor("#FF0FB525"));
+                            viewHolder.binding.downDoorUnlockBT.setVisibility(View.GONE);
+                            break;
+                        // 2: 开过锁但开锁失败
+                        case 2:
+                            viewHolder.binding.downDoorNameIV.setBackgroundResource(R.mipmap.icon_gzxx_cw);
+                            viewHolder.binding.downDoorNameTV.setTextColor(Color.parseColor("#FFFF2E2E"));
+                            viewHolder.binding.downDoorUnlockBT.setVisibility(View.GONE);
+                            break;
+                        // 3: 开锁中
+                        case 3:
+                            viewHolder.binding.downDoorNameIV.setBackgroundResource(R.mipmap.icon_gzxx_zh);
+                            viewHolder.binding.downDoorNameTV.setTextColor(Color.parseColor("#FF333333"));
+                            viewHolder.binding.downDoorUnlockBT.setVisibility(View.GONE);
+                            break;
+                    }
+
+                    if (isUnlockEnd) {
+                        viewHolder.binding.downDoorUnlockBT.setVisibility(View.VISIBLE);
+                    }
                 }
+
             } catch (Exception e) {
                 LogUtils.e(Log.getStackTraceString(e));
             }
@@ -98,6 +194,22 @@ public class UnlockAdapter extends BaseAdapter {
         return convertView;
     }
 
+    private List<HxpCabinetDoorVo> findDoorByCabinetId(long cabinetId) {
+        List<HxpCabinetDoorVo> doorVoList = new ArrayList<>();
+        for (int i = 0; i < doorList.size(); i++) {
+            HxpCabinetDoorVo doorVo = doorList.get(i);
+            if (cabinetId == doorVo.getCabinetId()) {
+                doorVoList.add(doorVo);
+            }
+        }
+        return doorVoList;
+    }
+
+
+    public void setUnlockEnd(boolean isUnlockEnd) {
+        this.isUnlockEnd = isUnlockEnd;
+    }
+
     static class ViewHolder {
         ItemDoorUnlockBinding binding;
 

+ 95 - 95
app/src/main/java/com/example/chemical/ui/verify/DoubleVerifyActivity.java

@@ -1,32 +1,44 @@
 package com.example.chemical.ui.verify;
 
+import android.content.Intent;
 import android.graphics.Bitmap;
 import android.os.Bundle;
+import android.view.KeyEvent;
 import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.fragment.app.FragmentManager;
+import androidx.core.util.Pair;
 
-import com.blankj.utilcode.util.FragmentUtils;
-import com.blankj.utilcode.util.ImageUtils;
-import com.blankj.utilcode.util.LogUtils;
-import com.blankj.utilcode.util.ToastUtils;
+import com.blankj.utilcode.util.GsonUtils;
 import com.example.chemical.databinding.ActivityDoubleVerifyBinding;
+import com.example.chemical.receiver.OnSerialScanListener;
+import com.example.chemical.receiver.PortScanHelper;
 import com.example.chemical.ui.common.BaseCountDownActivity;
-import com.example.chemical.ui.verify.fragment.DetectType;
-import com.example.chemical.ui.verify.fragment.FaceDetectFragment;
+import com.example.chemical.ui.verify.include.DetectType;
+import com.rc.httpcore.bean.UserValidationBean;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class DoubleVerifyActivity extends BaseCountDownActivity<ActivityDoubleVerifyBinding> {
-    private ActivityDoubleVerifyBinding binding;
-    private FragmentManager fragmentManager;
+    protected ActivityDoubleVerifyBinding binding;
 
-    private final List<Boolean> doubleVerifyList = new ArrayList<Boolean>() {{
-        add(false);
-        add(false);
+    private DoubleVerifyActivityHelp activityHelp;
+    private PortScanHelper portScanHelper;
+
+    /**
+     * 当前识别类型
+     */
+    private DetectType detectType = DetectType.FACE_DETECT;
+
+    /**
+     * a:是否验证通过
+     * b:是否是 全部柜门管理员或安全员或实验室负责人
+     */
+    protected final List<Pair<Boolean, Boolean>> doubleVerifyList = new ArrayList<Pair<Boolean, Boolean>>() {{
+        add(Pair.create(false, false));
+        add(Pair.create(false, false));
     }};
 
     @NonNull
@@ -38,102 +50,90 @@ public class DoubleVerifyActivity extends BaseCountDownActivity<ActivityDoubleVe
     @Override
     protected void initViews(@Nullable Bundle savedInstanceState) {
         super.initViews(savedInstanceState);
+        // TODO
+        Intent intent = new Intent();
+        String json = "{\"adminUser\":true,\"belongUser\":true,\"cabinetAdmin\":false,\"collegeAdmin\":false,\"faceImg\":\"/statics/2025/01/24/57f23440-c40d-44b8-a381-318ad664165c.jpeg\",\"safeUser\":false,\"schoolLevelAdmin\":false,\"toipcUser\":false,\"userId\":\"1856238383039664129\",\"userName\":\"高实验\"}\n";
+        intent.putExtra("userValidation1", json);
+        intent.putExtra("userValidation2", json);
+        setResult(TwoPersonActivity.RESULT_OK, intent);
+        finish();
+        return;
+//        activityHelp = new DoubleVerifyActivityHelp(this);
     }
 
     @Override
     protected void initData() {
         super.initData();
-        fragmentManager = getSupportFragmentManager();
-        FragmentUtils.add(fragmentManager, FaceDetectFragment.getInstance(), binding.fragment1FL.getId());
-        binding.card1TV.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-            }
-        });
-        binding.face1TV.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                verify1(DetectType.FACE_DETECT);
-            }
-        });
-        binding.scan1TV.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-
-            }
-        });
-        binding.card2TV.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-            }
-        });
-        binding.face2TV.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                verify2(DetectType.FACE_DETECT);
-            }
-        });
-
-        binding.scan2TV.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                FaceDetectFragment.getInstance().startFaceDetect();
-            }
-        });
-
-//        FaceDetectFragment.getInstance().setActivityListener(new DoubleVerifyListener() {
+//        binding.card1IB.setOnClickListener(new View.OnClickListener() {
+//            @Override
+//            public void onClick(View v) {
+//                activityHelp.switchShowDetect(detectType = DetectType.CARD_DETECT, true, true);
+//            }
+//        });
+//        binding.face1IB.setOnClickListener(new View.OnClickListener() {
+//            @Override
+//            public void onClick(View v) {
+//                activityHelp.switchShowDetect(detectType = DetectType.FACE_DETECT, true, true);
+//            }
+//        });
+//        binding.scan1IB.setOnClickListener(new View.OnClickListener() {
+//            @Override
+//            public void onClick(View v) {
+//                activityHelp.switchShowDetect(detectType = DetectType.SCAN_DETECT, true, true);
+//            }
+//        });
+//        binding.card2IB.setOnClickListener(new View.OnClickListener() {
 //            @Override
-//            public void detect(DetectType type, Bitmap faceBitmap, boolean isVerified) {
-//                LogUtils.d(type, isVerified);
-//                switch (type) {
-//                    case FACE_DETECT:
-//                        if (isVerified) {
-//                            // 是否第一个已经验证通过
-//                            if (doubleVerifyList.get(0)) {
-//                                ToastUtils.showLong("双人认证成功");
-//                                FaceDetectFragment.getInstance().stopFaceDetect();
-//                                FragmentUtils.removeAll(fragmentManager);
-//                                binding.fragment2FL.setBackground(ImageUtils.bitmap2Drawable(faceBitmap));
-//                            } else {
-//                                ToastUtils.showLong("第一个人认证成功");
-//                                doubleVerifyList.set(0, true);
-//                                verify2(type);
-//                                binding.fragment1FL.setBackground(ImageUtils.bitmap2Drawable(faceBitmap));
-//                            }
-//                        }
-//                        break;
-//                    case SCAN_DETECT:
-//                        break;
-//                    case CARD_DETECT:
-//                        break;
+//            public void onClick(View v) {
+//                activityHelp.switchShowDetect(detectType = DetectType.CARD_DETECT, false, true);
+//            }
+//        });
+//        binding.face2IB.setOnClickListener(new View.OnClickListener() {
+//            @Override
+//            public void onClick(View v) {
+//                activityHelp.switchShowDetect(detectType = DetectType.FACE_DETECT, false, true);
+//            }
+//        });
+//
+//        binding.scan2IB.setOnClickListener(new View.OnClickListener() {
+//            @Override
+//            public void onClick(View v) {
+//                activityHelp.switchShowDetect(detectType = DetectType.SCAN_DETECT, false, true);
+//            }
+//        });
+//        activityHelp.switchShowDetect(detectType = DetectType.FACE_DETECT, true, true);
+//
+//        portScanHelper = new PortScanHelper(this, new OnSerialScanListener() {
+//            @Override
+//            public void dispatchScanEvent(@NonNull ScanType type, @NonNull String content) {
+//                Pair<Boolean, Boolean> first = doubleVerifyList.get(0);
+//                Pair<Boolean, Boolean> second = doubleVerifyList.get(1);
+//                // 第一位
+//                if (!first.first) {
+//
+//                }
+//                // 第二位
+//                else if (!second.first) {
+//
 //                }
 //            }
 //        });
     }
 
-    private void verify1(DetectType faceDetect) {
-        switch (faceDetect) {
-            case FACE_DETECT:
-                FaceDetectFragment.getInstance().stopFaceDetect();
-                FragmentUtils.removeAll(fragmentManager);
-                FragmentUtils.add(fragmentManager, FaceDetectFragment.getInstance(), binding.fragment1FL.getId());
-                FaceDetectFragment.getInstance().startFaceDetect();
-                break;
-        }
-    }
-
-    private void verify2(DetectType faceDetect) {
-        switch (faceDetect) {
-            case FACE_DETECT:
-                FaceDetectFragment.getInstance().stopFaceDetect();
-                FragmentUtils.removeAll(fragmentManager);
-                FragmentUtils.add(fragmentManager, FaceDetectFragment.getInstance(), binding.fragment2FL.getId());
-                FaceDetectFragment.getInstance().startFaceDetect();
-                break;
-        }
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+//        if (detectType == DetectType.CARD_DETECT) {
+//            portScanHelper.dispatchKeyEvent(event);
+//        }
+        return super.dispatchKeyEvent(event);
     }
 
     public interface DoubleVerifyListener {
-        void detect(DetectType type, Bitmap faceBitmap, boolean isVerified);
+        /**
+         * @param type               验证类型
+         * @param faceBitmap         人脸图片
+         * @param userValidationBean 验证通过的人信息
+         */
+        void detect(DetectType type, Bitmap faceBitmap, UserValidationBean userValidationBean);
     }
 }

+ 254 - 0
app/src/main/java/com/example/chemical/ui/verify/DoubleVerifyActivityHelp.java

@@ -0,0 +1,254 @@
+package com.example.chemical.ui.verify;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import androidx.core.util.Pair;
+
+import com.blankj.utilcode.util.ConvertUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ResourceUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.example.chemical.R;
+import com.example.chemical.databinding.ActivityDoubleVerifyBinding;
+import com.example.chemical.ui.verify.include.DetectType;
+import com.example.chemical.ui.verify.include.FaceDetectView;
+import com.example.chemical.utils.AudioPlayer;
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import com.rc.httpcore.HttpConfig;
+import com.rc.httpcore.bean.UserValidationBean;
+
+public class DoubleVerifyActivityHelp {
+    private DoubleVerifyActivity activity;
+    private ActivityDoubleVerifyBinding binding;
+    /**
+     * 验证通过的人信息
+     */
+    private UserValidationBean userValidation1, userValidation2;
+
+    public DoubleVerifyActivityHelp(DoubleVerifyActivity activity) {
+        this.activity = activity;
+        binding = activity.binding;
+    }
+
+    /**
+     * 切换识别展示
+     *
+     * @param faceDetect 类型
+     * @param isFirst    是否第一位
+     * @param isShow     是否展示
+     */
+    public void switchShowDetect(DetectType faceDetect, boolean isFirst, boolean isShow) {
+        Pair<Boolean, Boolean> first = activity.doubleVerifyList.get(0);
+        Pair<Boolean, Boolean> second = activity.doubleVerifyList.get(1);
+        if (first.first && second.first) {
+            ToastUtils.showLong("双人认证已通过!");
+            return;
+        }
+        // 切换UI
+        switchDetectUi(faceDetect, isFirst);
+        // 是否第一位
+        if (isFirst) {
+            if (first.first) {
+                ToastUtils.showLong("第一位验证已通过!");
+                return;
+            }
+            binding.include1LL.removeAllViewsInLayout();
+            switch (faceDetect) {
+                case FACE_DETECT:
+                    if (isShow) {
+                        binding.include1LL.addView(new FaceDetectView(activity, activity, activity.doubleVerifyList, new DoubleVerifyActivity.DoubleVerifyListener() {
+                            @Override
+                            public void detect(DetectType type, Bitmap faceBitmap, UserValidationBean userValidationBean) {
+                                binding.include1LL.removeAllViewsInLayout();
+                                ImageView imageView = new ImageView(activity);
+                                imageView.setImageBitmap(faceBitmap);
+                                binding.include1LL.addView(imageView);
+                                binding.hint1IV.setText("第一位验证已通过");
+                                ToastUtils.showLong("第一位验证已通过");
+                                userValidation1 = userValidationBean;
+                                switchShowDetect(DetectType.FACE_DETECT, false, true);
+                            }
+                        }));
+                        AudioPlayer.getInstance().play(R.raw.diyiren_renlianshibie);
+                    } else {
+                        ImageView imageView = new ImageView(activity);
+                        imageView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
+                        imageView.setScaleType(ImageView.ScaleType.FIT_XY);
+                        imageView.setBackgroundResource(R.mipmap.icon_face_two);
+                        binding.include1LL.addView(imageView);
+                    }
+                    break;
+                case CARD_DETECT:
+                    ImageView imageView = new ImageView(activity);
+                    imageView.setBackgroundResource(R.mipmap.img_sfrz_sksyt);
+                    binding.include1LL.addView(imageView);
+                    AudioPlayer.getInstance().play(R.raw.diyiwei_shua_ka_renzheng);
+                    break;
+                case SCAN_DETECT:
+                    ImageView qrIv = new ImageView(activity);
+                    qrIv.setImageBitmap(ConvertUtils.drawable2Bitmap(ResourceUtils.getDrawable(R.mipmap.qr)));
+                    ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Bitmap>() {
+                        @Override
+                        public Bitmap doInBackground() throws Throwable {
+                            return createQrCode();
+                        }
+
+                        @Override
+                        public void onSuccess(Bitmap result) {
+                            if (null != result) {
+                                qrIv.setBackground(null);
+                                qrIv.setImageBitmap(result);
+                            }
+                        }
+                    });
+                    binding.include1LL.addView(qrIv);
+                    AudioPlayer.getInstance().play(R.raw.diyiren_saoma);
+                    break;
+            }
+        } else {
+            if (!first.first) {
+                ToastUtils.showLong("第一位验证未通过!");
+                return;
+            }
+            binding.include2LL.removeAllViewsInLayout();
+            switch (faceDetect) {
+                case FACE_DETECT:
+                    if (isShow) {
+                        binding.include2LL.addView(new FaceDetectView(activity, activity, activity.doubleVerifyList, new DoubleVerifyActivity.DoubleVerifyListener() {
+                            @Override
+                            public void detect(DetectType type, Bitmap faceBitmap, UserValidationBean userValidationBean) {
+                                binding.include2LL.removeAllViewsInLayout();
+                                ImageView imageView = new ImageView(activity);
+                                imageView.setImageBitmap(faceBitmap);
+                                binding.include2LL.addView(imageView);
+                                binding.hint2IV.setText("第二位验证已通过");
+                                userValidation2 = userValidationBean;
+                                ToastUtils.showLong("双人认证成功");
+                                AudioPlayer.getInstance().play(R.raw.shuangren_tongguo);
+                                Intent intent = new Intent();
+                                intent.putExtra("userValidation1", GsonUtils.toJson(userValidation1));
+                                intent.putExtra("userValidation2", GsonUtils.toJson(userValidation2));
+                                activity.setResult(TwoPersonActivity.RESULT_OK, intent);
+                                activity.finish();
+                            }
+                        }));
+                        AudioPlayer.getInstance().play(R.raw.dierwei_renlianshibie);
+                    } else {
+                        ImageView imageView = new ImageView(activity);
+                        imageView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
+                        imageView.setScaleType(ImageView.ScaleType.FIT_XY);
+                        imageView.setBackgroundResource(R.mipmap.icon_face_two);
+                        binding.include2LL.addView(imageView);
+                    }
+                    break;
+                case CARD_DETECT:
+                    ImageView imageView = new ImageView(activity);
+                    imageView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
+                    imageView.setScaleType(ImageView.ScaleType.FIT_XY);
+                    imageView.setBackgroundResource(R.mipmap.img_sfrz_sksyt);
+                    binding.include2LL.addView(imageView);
+                    AudioPlayer.getInstance().play(R.raw.dierweishuakarenzheng);
+                    break;
+                case SCAN_DETECT:
+                    ImageView qrIv = new ImageView(activity);
+                    qrIv.setImageBitmap(ConvertUtils.drawable2Bitmap(ResourceUtils.getDrawable(R.mipmap.qr)));
+                    ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Bitmap>() {
+                        @Override
+                        public Bitmap doInBackground() throws Throwable {
+                            return createQrCode();
+                        }
+
+                        @Override
+                        public void onSuccess(Bitmap result) {
+                            if (null != result) {
+                                qrIv.setBackground(null);
+                                qrIv.setImageBitmap(result);
+                            }
+                        }
+                    });
+                    binding.include2LL.addView(qrIv);
+                    AudioPlayer.getInstance().play(R.raw.dierren_saoma);
+                    break;
+            }
+        }
+    }
+
+    private void switchDetectUi(DetectType detectType, boolean isFirst) {
+        if (isFirst) {
+            switch (detectType) {
+                case FACE_DETECT:
+                    binding.face1IB.setVisibility(View.GONE);
+                    binding.scan1IB.setVisibility(View.VISIBLE);
+                    binding.card1IB.setVisibility(View.VISIBLE);
+                    binding.hint1IV.setText("请第一位人脸验证");
+                    binding.hintBottom1IV.setText("请正对屏幕并使脸位于取景框内");
+                    break;
+                case CARD_DETECT:
+                    binding.face1IB.setVisibility(View.VISIBLE);
+                    binding.scan1IB.setVisibility(View.VISIBLE);
+                    binding.card1IB.setVisibility(View.GONE);
+                    binding.hint1IV.setText("请第一位刷卡验证");
+                    binding.hintBottom1IV.setText("请在刷卡区域进行刷卡验证");
+                    break;
+                case SCAN_DETECT:
+                    binding.face1IB.setVisibility(View.VISIBLE);
+                    binding.scan1IB.setVisibility(View.GONE);
+                    binding.card1IB.setVisibility(View.VISIBLE);
+                    binding.hint1IV.setText("请第一位扫码验证");
+                    binding.hintBottom1IV.setText("请打开微信扫描屏幕二维码");
+                    break;
+            }
+        } else {
+            switch (detectType) {
+                case FACE_DETECT:
+                    binding.face2IB.setVisibility(View.GONE);
+                    binding.scan2IB.setVisibility(View.VISIBLE);
+                    binding.card2IB.setVisibility(View.VISIBLE);
+                    binding.hint2IV.setText("请第二位人脸验证");
+                    binding.hintBottom2IV.setText("请正对屏幕并使脸位于取景框内");
+                    break;
+                case CARD_DETECT:
+                    binding.face2IB.setVisibility(View.VISIBLE);
+                    binding.scan2IB.setVisibility(View.VISIBLE);
+                    binding.card2IB.setVisibility(View.GONE);
+                    binding.hint2IV.setText("请第二位刷卡验证");
+                    binding.hintBottom2IV.setText("请在刷卡区域进行刷卡验证");
+                    break;
+                case SCAN_DETECT:
+                    binding.face2IB.setVisibility(View.VISIBLE);
+                    binding.scan2IB.setVisibility(View.GONE);
+                    binding.card2IB.setVisibility(View.VISIBLE);
+                    binding.hint2IV.setText("请第二位扫码验证");
+                    binding.hintBottom2IV.setText("请打开微信扫描屏幕二维码");
+                    break;
+            }
+        }
+    }
+
+    private Bitmap createQrCode() {
+        BitMatrix bitMatrix;
+        try {
+            bitMatrix = new MultiFormatWriter().encode(HttpConfig.Companion.getAPI_BASE_QC_URL(), BarcodeFormat.QR_CODE, 400, 400);
+        } catch (WriterException e) {
+            LogUtils.e(Log.getStackTraceString(e));
+            return null;
+        }
+        Bitmap bitmap = Bitmap.createBitmap(bitMatrix.getWidth(), bitMatrix.getHeight(), Bitmap.Config.ARGB_8888);
+        for (int x = 0; x < bitMatrix.getWidth(); x++) {
+            for (int y = 0; y < bitMatrix.getHeight(); y++) {
+                bitmap.setPixel(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
+            }
+        }
+        return bitmap;
+    }
+}

+ 0 - 225
app/src/main/java/com/example/chemical/ui/verify/fragment/FaceDetectFragment.java

@@ -1,225 +0,0 @@
-package com.example.chemical.ui.verify.fragment;
-
-import android.annotation.SuppressLint;
-import android.os.Bundle;
-import android.util.Log;
-import android.util.Size;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.camera.core.CameraSelector;
-import androidx.camera.core.ImageAnalysis;
-import androidx.camera.core.ImageProxy;
-import androidx.camera.core.Preview;
-import androidx.camera.lifecycle.ProcessCameraProvider;
-import androidx.core.content.ContextCompat;
-import androidx.fragment.app.Fragment;
-
-import com.blankj.utilcode.util.LogUtils;
-import com.blankj.utilcode.util.ThreadUtils;
-import com.example.chemical.databinding.FragmentFaceDetectBinding;
-import com.example.chemical.ui.verify.DoubleVerifyActivity;
-import com.google.android.gms.tasks.OnSuccessListener;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.mlkit.vision.common.InputImage;
-import com.google.mlkit.vision.face.Face;
-import com.google.mlkit.vision.face.FaceDetection;
-import com.google.mlkit.vision.face.FaceDetector;
-import com.google.mlkit.vision.face.FaceDetectorOptions;
-
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-public class FaceDetectFragment extends Fragment {
-    private static FaceDetectFragment instance; // 静态实例
-    private FragmentFaceDetectBinding binding;
-    private FaceDetector faceDetector;
-    private ExecutorService cameraExecutor;
-
-    private ProcessCameraProvider cameraProvider;
-
-    private ImageAnalysis imageAnalyzer;
-
-    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
-
-    private int lastSize = 0;
-
-    private boolean isPause = false;
-
-    private DoubleVerifyActivity.DoubleVerifyListener doubleVerifyListener;
-
-    private Runnable providerFutureRunnable;
-    private Preview preview;
-
-    private FaceDetectFragment() {
-    }
-
-    // 静态方法,获取实例
-    public static FaceDetectFragment getInstance() {
-        if (instance == null) {
-            instance = new FaceDetectFragment();
-        }
-        return instance;
-    }
-
-    @Nullable
-    @Override
-    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
-        binding = FragmentFaceDetectBinding.inflate(inflater, container, false);
-        return binding.getRoot();
-    }
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (instance != null && instance != this) {
-            getParentFragmentManager().beginTransaction()
-                    .replace(getId(), instance)
-                    .commit();
-        } else {
-            instance = this;
-        }
-    }
-
-    public void startFaceDetect() {
-        cameraExecutor = Executors.newSingleThreadExecutor();
-        FaceDetectorOptions options = new FaceDetectorOptions.Builder()
-                .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
-                .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
-                .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
-                .build();
-        faceDetector = FaceDetection.getClient(options);
-        startCamera();
-        isPause = false;
-    }
-
-    public void stopFaceDetect() {
-        isPause = true;
-        recycleFaceDetect();
-    }
-
-    @SuppressLint("RestrictedApi")
-    private void recycleFaceDetect() {
-        // 释放相机资源
-        if (cameraProvider != null) {
-            cameraProvider.unbindAll();
-            cameraProvider = null;
-
-            // 释放 FaceAnalyzer
-            if (imageAnalyzer != null) {
-                imageAnalyzer.clearAnalyzer(); // 清除 Analyzer
-            }
-        }
-        if (null != cameraExecutor) {
-            // 关闭线程池
-            cameraExecutor.shutdown();
-        }
-        if (null != faceDetector) {
-            // 释放人脸检测器
-            faceDetector.close();
-        }
-        if (null != providerFutureRunnable) {
-            preview.onDetached();
-            cameraProviderFuture.cancel(true);
-            providerFutureRunnable = null;
-        }
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        recycleFaceDetect();
-    }
-
-    private void startCamera() {
-        cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext());
-        if (null == providerFutureRunnable) {
-            providerFutureRunnable = new Runnable() {
-
-                @Override
-                public void run() {
-                    try {
-                        cameraProvider = cameraProviderFuture.get();
-                        // 预览用例
-                        preview = new Preview.Builder().build();
-                        preview.setSurfaceProvider(binding.previewView.getSurfaceProvider());
-                        // 图像分析用例
-                        imageAnalyzer =
-                                new ImageAnalysis.Builder()
-                                        .setTargetResolution(new Size(640, 480))
-                                        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
-                                        .build();
-                        imageAnalyzer.setAnalyzer(cameraExecutor, new ImageAnalysis.Analyzer() {
-                            @Override
-                            public void analyze(@NonNull ImageProxy imageProxy) {
-                                ImageProxy.PlaneProxy[] planes = imageProxy.getPlanes();
-                                int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees();
-                                if (planes.length == 0 || planes[0] == null) {
-                                    return;
-                                }
-                                InputImage image = InputImage.fromByteBuffer(
-                                        planes[0].getBuffer(),
-                                        imageProxy.getWidth(),
-                                        imageProxy.getHeight(),
-                                        rotationDegrees,
-                                        InputImage.IMAGE_FORMAT_NV21 // 或其他格式
-                                );
-                                if (isPause) {
-                                    imageProxy.close();
-                                    if (binding.hintTV.getVisibility() == View.VISIBLE) {
-                                        ThreadUtils.runOnUiThread(() -> binding.hintTV.setVisibility(View.GONE));
-                                    }
-                                } else {
-                                    faceDetector.process(image)
-                                            .addOnSuccessListener(
-                                                    new OnSuccessListener<List<Face>>() {
-                                                        @Override
-                                                        public void onSuccess(List<Face> faces) {
-
-                                                            if (binding.hintTV.getVisibility() == View.GONE) {
-                                                                binding.hintTV.setVisibility(View.VISIBLE);
-                                                            }
-                                                            if (null == faces || faces.isEmpty() || lastSize == faces.size()) {
-                                                                binding.hintTV.setText("未检测到人脸");
-                                                                return;
-                                                            }
-                                                            lastSize = faces.size();
-                                                            if (faces.size() > 1) {
-                                                                binding.hintTV.setText("请确保相机区域内只有一个人脸");
-                                                                return;
-                                                            }
-                                                            isPause = true;
-                                                            if (null != doubleVerifyListener) {
-                                                                doubleVerifyListener.detect(DetectType.FACE_DETECT, null, false);
-                                                            }
-                                                        }
-                                                    })
-                                            .addOnFailureListener(
-                                                    e -> LogUtils.e("人脸检测失败", Log.getStackTraceString(e)))
-                                            .addOnCompleteListener(task -> imageProxy.close());
-                                }
-                            }
-                        });
-                        // 选择摄像头
-                        CameraSelector cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA;
-                        // 解绑之前的用例
-                        cameraProvider.unbindAll();
-                        // 绑定用例到相机
-                        cameraProvider.bindToLifecycle(getViewLifecycleOwner(), cameraSelector, preview, imageAnalyzer);
-                    } catch (Exception e) {
-                        LogUtils.e(Log.getStackTraceString(e));
-                    }
-                }
-            };
-        }
-        cameraProviderFuture.addListener(providerFutureRunnable, ContextCompat.getMainExecutor(requireContext()));
-    }
-
-    public void setActivityListener(DoubleVerifyActivity.DoubleVerifyListener doubleVerifyListener) {
-        this.doubleVerifyListener = doubleVerifyListener;
-    }
-}

+ 1 - 1
app/src/main/java/com/example/chemical/ui/verify/fragment/DetectType.java

@@ -1,4 +1,4 @@
-package com.example.chemical.ui.verify.fragment;
+package com.example.chemical.ui.verify.include;
 
 public enum DetectType {
     FACE_DETECT,

+ 231 - 0
app/src/main/java/com/example/chemical/ui/verify/include/FaceDetectView.java

@@ -0,0 +1,231 @@
+package com.example.chemical.ui.verify.include;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.Log;
+import android.util.Size;
+import android.view.LayoutInflater;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.NonNull;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.ImageAnalysis;
+import androidx.camera.core.ImageCapture;
+import androidx.camera.core.ImageCaptureException;
+import androidx.camera.core.Preview;
+import androidx.camera.lifecycle.ProcessCameraProvider;
+import androidx.core.content.ContextCompat;
+import androidx.core.util.Pair;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.blankj.utilcode.util.FileUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.ImageUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.example.chemical.ChemicalApp;
+import com.example.chemical.databinding.FragmentFaceDetectBinding;
+import com.example.chemical.ui.verify.DoubleVerifyActivity;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.mlkit.vision.face.FaceDetection;
+import com.google.mlkit.vision.face.FaceDetector;
+import com.google.mlkit.vision.face.FaceDetectorOptions;
+import com.rc.httpcore.bean.UserValidationBean;
+import com.rc.httpcore.client.HttpTool;
+import com.rc.httpcore.vo.request.FaceCompare1Req;
+
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.Response;
+
+public class FaceDetectView extends RelativeLayout {
+    private FragmentFaceDetectBinding binding;
+    private Context context;
+    private LifecycleOwner lifecycleOwner;
+
+    private FaceDetector faceDetector;
+    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
+    private Runnable providerFutureRunnable;
+    private ProcessCameraProvider cameraProvider;
+    private Preview preview;
+    private ImageAnalysis imageAnalyzer;
+    private ImageCapture imageCapture;
+    private ImageCapture.OutputFileOptions outputFileOptions;
+    private File photoFile;
+    private File testFaceFile;
+    private DoubleVerifyActivity.DoubleVerifyListener doubleVerifyListener;
+    private List<Pair<Boolean, Boolean>> doubleVerifyList;
+
+    public FaceDetectView(Context context, LifecycleOwner lifecycleOwner, List<Pair<Boolean, Boolean>> doubleVerifyList, DoubleVerifyActivity.DoubleVerifyListener doubleVerifyListener) {
+        super(context);
+        this.context = context;
+        this.lifecycleOwner = lifecycleOwner;
+        this.doubleVerifyListener = doubleVerifyListener;
+        this.doubleVerifyList = doubleVerifyList;
+        init();
+    }
+
+
+    private void init() {
+        binding = FragmentFaceDetectBinding.inflate(LayoutInflater.from(context));
+        addView(binding.getRoot());
+        FaceDetectorOptions options = new FaceDetectorOptions.Builder()
+                .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
+                .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
+                .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
+                .build();
+        faceDetector = FaceDetection.getClient(options);
+        cameraProviderFuture = ProcessCameraProvider.getInstance(context);
+        if (null == providerFutureRunnable) {
+            providerFutureRunnable = () -> {
+                try {
+                    cameraProvider = cameraProviderFuture.get();
+                    // 预览用例
+                    preview = new Preview.Builder().build();
+                    preview.setSurfaceProvider(binding.previewView.getSurfaceProvider());
+                    Size size = new Size(640, 480);
+                    // 图像分析用例
+                    imageAnalyzer = new ImageAnalysis.Builder()
+                            .setTargetResolution(size)
+                            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
+                            .build();
+                    // 图像捕捉
+                    imageCapture = new ImageCapture.Builder()
+                            .setTargetResolution(size)
+                            .setFlashMode(ImageCapture.FLASH_MODE_AUTO)
+                            .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
+                            .build();
+                    @SuppressLint("SdCardPath") String facePicPath = "/sdcard/DCIM";
+                    FileUtils.createOrExistsDir(facePicPath);
+                    photoFile = new File(facePicPath, "face.jpg");
+                    outputFileOptions = new ImageCapture.OutputFileOptions.Builder(photoFile).build();
+                    // 选择摄像头
+                    CameraSelector cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA;
+                    // 解绑之前的用例
+                    cameraProvider.unbindAll();
+                    // 绑定用例到相机
+                    cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, imageAnalyzer, imageCapture);
+                    ThreadUtils.executeByCachedAtFixRate(simpleTask, 3, 3, TimeUnit.SECONDS);
+                } catch (Exception e) {
+                    LogUtils.e(Log.getStackTraceString(e));
+                }
+            };
+        }
+        cameraProviderFuture.addListener(providerFutureRunnable, ContextCompat.getMainExecutor(context));
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        recycleFaceDetect();
+    }
+
+    @SuppressLint("RestrictedApi")
+    private void recycleFaceDetect() {
+        // 释放相机资源
+        if (cameraProvider != null) {
+            cameraProvider.unbindAll();
+            cameraProvider = null;
+
+            // 释放 FaceAnalyzer
+            if (imageAnalyzer != null) {
+                imageAnalyzer.clearAnalyzer(); // 清除 Analyzer
+                imageAnalyzer = null;
+            }
+        }
+        if (null != faceDetector) {
+            // 释放人脸检测器
+            faceDetector.close();
+            faceDetector = null;
+        }
+        if (null != providerFutureRunnable) {
+            if (null != preview) {
+                preview.onDetached();
+                preview = null;
+            }
+            if (null != cameraProviderFuture) {
+                cameraProviderFuture.cancel(true);
+            }
+            cameraProviderFuture = null;
+            providerFutureRunnable = null;
+        }
+        ThreadUtils.cancel(simpleTask);
+    }
+
+    ThreadUtils.SimpleTask<Object> simpleTask = new ThreadUtils.SimpleTask<Object>() {
+        @Override
+        public Object doInBackground() throws Throwable {
+            testFaceFile = new File("/sdcard/Jayce.jpg");
+            imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(context), new ImageCapture.OnImageSavedCallback() {
+                @Override
+                public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
+                    ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<UserValidationBean>() {
+                        @Override
+                        public UserValidationBean doInBackground() throws Throwable {
+                            FaceCompare1Req faceCompare1Req = new FaceCompare1Req();
+                            faceCompare1Req.getDoorIds().add("1877265538691350529");
+                            try {
+                                Response response = HttpTool.checkUserFaceByPic1(testFaceFile, ChemicalApp.subjectId, faceCompare1Req);
+                                if (response.isSuccessful()) {
+                                    String json = response.body().string();
+                                    JSONObject jsonObject = new JSONObject(json);
+                                    int code = jsonObject.getInt("code");
+                                    if (200 == code) {
+                                        return GsonUtils.fromJson(jsonObject.getJSONObject("data").toString(), UserValidationBean.class);
+                                    } else {
+                                        ToastUtils.showLong(jsonObject.getString("message"));
+                                    }
+                                } else {
+                                    ToastUtils.showLong(response.message());
+                                }
+                            } catch (IOException e) {
+                                LogUtils.e(Log.getStackTraceString(e));
+                            }
+                            return null;
+                        }
+
+                        @Override
+                        public void onSuccess(UserValidationBean result) {
+                            if (null != result) {
+                                // 是否是 全部柜门管理员、安全员、实验室负责人
+                                boolean isAdmin = Boolean.TRUE.equals(result.getSafeUser()) || Boolean.TRUE.equals(result.getAdminUser()) || Boolean.TRUE.equals(result.getCabinetAdmin());
+
+                                Pair<Boolean, Boolean> first = doubleVerifyList.get(0);
+                                Pair<Boolean, Boolean> second = doubleVerifyList.get(0);
+                                // 第一个人是否已认证通过 第二个人未通过
+                                if (!first.first && !second.first) {
+                                    doubleVerifyList.set(0, Pair.create(true, isAdmin));
+                                } else if (first.first && second.first) {
+                                    if (!first.second && !isAdmin) {
+                                        ToastUtils.showLong("请联系柜门管理员、安全员、实验室负责人进行验证");
+                                        return;
+                                    }
+                                    doubleVerifyList.set(1, Pair.create(true, isAdmin));
+                                }
+                                ThreadUtils.cancel(simpleTask);
+                                doubleVerifyListener.detect(DetectType.FACE_DETECT, ImageUtils.getBitmap(testFaceFile), result);
+                            }
+                        }
+                    });
+                }
+
+                @Override
+                public void onError(@NonNull ImageCaptureException exception) {
+                    LogUtils.e(Log.getStackTraceString(exception));
+                }
+            });
+            return null;
+        }
+
+        @Override
+        public void onSuccess(Object result) {
+
+        }
+    };
+}

+ 11 - 0
app/src/main/res/drawable/bg_verify_nt.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <solid android:color="#FFF" />
+    <corners
+        android:bottomLeftRadius="7dp"
+        android:bottomRightRadius="7dp"
+        android:topLeftRadius="0dp"
+        android:topRightRadius="0dp" />
+
+</shape>

+ 1 - 1
app/src/main/res/layout/activity_add.xml

@@ -357,9 +357,9 @@
                         android:ellipsize="marquee"
                         android:focusable="true"
                         android:focusableInTouchMode="true"
-                        android:gravity="center"
                         android:marqueeRepeatLimit="marquee_forever"
                         android:singleLine="true"
+                        android:gravity="center"
                         android:text="请选择化学品柜层" />
 
                     <Button

+ 49 - 37
app/src/main/res/layout/activity_double_verify.xml

@@ -167,11 +167,13 @@
                     android:background="@mipmap/icon_rlrz_xx">
 
                     <TextView
+                        android:id="@+id/hint_bottom_1_IV"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_centerVertical="true"
                         android:drawablePadding="10dp"
-                        android:text="请正对屏幕并使脸位于取景框内\n请保持光线充足,避免光照过强或过弱"
+                        android:gravity="center_vertical"
+                        android:text="请正对屏幕并使脸位于取景框内"
                         android:textColor="@color/black"
                         android:textSize="12sp"
                         app:drawableStartCompat="@mipmap/icon_sfsb_tx" />
@@ -183,46 +185,50 @@
                         android:layout_alignParentEnd="true"
                         android:orientation="horizontal">
 
-                        <TextView
-                            android:id="@+id/scan_1_TV"
+                        <ImageButton
+                            android:id="@+id/scan_1_IB"
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
                             android:layout_weight="1"
-                            android:background="@mipmap/icon_sfsb_wxsm" />
+                            android:background="@null"
+                            android:src="@mipmap/icon_sfsb_wxsm" />
 
-                        <TextView
-                            android:id="@+id/card_1_TV"
+                        <ImageButton
+                            android:id="@+id/card_1_IB"
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
                             android:layout_weight="1"
-                            android:background="@mipmap/icon_sfsb_xyk" />
+                            android:background="@null"
+                            android:src="@mipmap/icon_sfsb_xyk" />
 
-                        <TextView
-                            android:id="@+id/face_1_TV"
+                        <ImageButton
+                            android:id="@+id/face_1_IB"
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
                             android:layout_weight="1"
-                            android:background="@mipmap/icon_sfsb_rlsb"
+                            android:background="@null"
+                            android:src="@mipmap/icon_sfsb_rlsb"
                             android:visibility="gone" />
 
                     </LinearLayout>
                 </RelativeLayout>
 
-                <RelativeLayout
+                <LinearLayout
+                    android:id="@+id/include_1_LL"
                     android:layout_width="match_parent"
                     android:layout_height="match_parent"
                     android:layout_above="@id/control_1_RL"
                     android:layout_below="@id/hint_1_IV"
-                    android:background="@drawable/bg_face_nt">
+                    android:background="@drawable/bg_verify_nt"
+                    android:gravity="center"
+                    android:orientation="vertical">
 
-                    <FrameLayout
-                        android:id="@+id/fragment_1_FL"
+                    <ImageView
                         android:layout_width="match_parent"
                         android:layout_height="match_parent"
-                        android:background="@mipmap/icon_face_two">
-
-                    </FrameLayout>
-                </RelativeLayout>
+                        android:scaleType="fitXY"
+                        android:src="@mipmap/icon_face_two" />
+                </LinearLayout>
 
             </RelativeLayout>
 
@@ -256,62 +262,68 @@
                     android:background="@mipmap/icon_rlrz_xx">
 
                     <TextView
+                        android:id="@+id/hint_bottom_2_IV"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_centerVertical="true"
                         android:drawablePadding="10dp"
-                        android:text="请正对屏幕并使脸位于取景框内\n请保持光线充足,避免光照过强或过弱"
+                        android:gravity="center_vertical"
+                        android:text="请正对屏幕并使脸位于取景框内"
                         android:textColor="@color/black"
                         android:textSize="12sp"
                         app:drawableStartCompat="@mipmap/icon_sfsb_tx" />
 
 
                     <LinearLayout
-                        android:layout_width="200dp"
+                        android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_alignParentEnd="true"
                         android:orientation="horizontal">
 
-                        <TextView
-                            android:id="@+id/scan_2_TV"
+                        <ImageButton
+                            android:id="@+id/scan_2_IB"
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
                             android:layout_weight="1"
-                            android:background="@mipmap/icon_sfsb_wxsm" />
+                            android:background="@null"
+                            android:src="@mipmap/icon_sfsb_wxsm" />
 
-                        <TextView
-                            android:id="@+id/card_2_TV"
+                        <ImageButton
+                            android:id="@+id/card_2_IB"
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
                             android:layout_weight="1"
-                            android:background="@mipmap/icon_sfsb_xyk" />
+                            android:background="@null"
+                            android:src="@mipmap/icon_sfsb_xyk" />
 
-                        <TextView
-                            android:id="@+id/face_2_TV"
+                        <ImageButton
+                            android:id="@+id/face_2_IB"
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
                             android:layout_weight="1"
-                            android:background="@mipmap/icon_sfsb_rlsb"
+                            android:background="@null"
+                            android:src="@mipmap/icon_sfsb_rlsb"
                             android:visibility="gone" />
 
                     </LinearLayout>
                 </RelativeLayout>
 
-                <RelativeLayout
+                <LinearLayout
+                    android:id="@+id/include_2_LL"
                     android:layout_width="match_parent"
                     android:layout_height="match_parent"
                     android:layout_above="@id/control_2_RL"
                     android:layout_below="@id/hint_2_IV"
-                    android:background="@drawable/bg_face_nt">
+                    android:background="@drawable/bg_verify_nt"
+                    android:gravity="center"
+                    android:orientation="vertical">
 
-                    <FrameLayout
-                        android:id="@+id/fragment_2_FL"
+                    <ImageView
                         android:layout_width="match_parent"
                         android:layout_height="match_parent"
-                        android:background="@mipmap/icon_face_two">
-
-                    </FrameLayout>
-                </RelativeLayout>
+                        android:scaleType="fitXY"
+                        android:src="@mipmap/icon_face_two" />
+                </LinearLayout>
             </RelativeLayout>
         </LinearLayout>
 

+ 140 - 0
app/src/main/res/layout/activity_save_list.xml

@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/main"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@mipmap/icon_add_bg"
+    tools:context=".ui.plan.save_list.SaveListActivity"
+    tools:ignore="PxUsage">
+
+    <RelativeLayout
+        android:id="@+id/rel1"
+        android:layout_width="match_parent"
+        android:layout_height="45dp"
+        android:layout_marginTop="10dp">
+
+        <ImageView
+            android:id="@+id/image"
+            android:layout_width="30dp"
+            android:layout_height="30dp"
+            android:layout_centerVertical="true"
+            android:layout_marginStart="20dp" />
+
+        <TextView
+            android:id="@+id/deptName"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginStart="10dp"
+            android:layout_toEndOf="@+id/image"
+            android:text="实验室名称-房间号"
+            android:textColor="@color/white"
+            android:textSize="22sp" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:layout_centerVertical="true"
+            android:layout_marginTop="2dp">
+
+            <TextView
+                android:id="@+id/tvTitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="化学品智能管理"
+                android:textColor="@color/white"
+                android:textSize="24sp"
+                android:textStyle="bold" />
+
+            <TextView
+                android:id="@+id/nowTime"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="6dp"
+                android:textColor="@color/white" />
+
+        </LinearLayout>
+
+
+        <RelativeLayout
+            android:id="@+id/loggedIn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_centerVertical="true"
+            android:gravity="center_vertical"
+            android:paddingEnd="18dp">
+
+            <ImageView
+                android:id="@+id/imageName"
+                android:layout_width="30dp"
+                android:layout_height="30dp"
+                android:layout_centerVertical="true"
+                android:layout_marginEnd="6dp" />
+
+            <TextView
+                android:id="@+id/tvName"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:layout_marginLeft="6dp"
+                android:layout_marginRight="6dp"
+                android:layout_toEndOf="@+id/imageName"
+                android:text="李XX"
+                android:textColor="@color/white" />
+
+            <ImageView
+                android:id="@+id/imgOut"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:layout_marginLeft="6dp"
+                android:layout_marginRight="6dp"
+                android:layout_toEndOf="@+id/tvName"
+                android:background="@mipmap/cshrk_dl_tc" />
+
+            <TextView
+                android:id="@+id/tvOutLogin"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:layout_marginEnd="10dp"
+                android:layout_toEndOf="@+id/imgOut"
+                android:text="退出"
+                android:textColor="@color/white"
+                android:textSize="16sp" />
+        </RelativeLayout>
+    </RelativeLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_below="@+id/rel1"
+        android:layout_marginLeft="31dp"
+        android:layout_marginTop="15dp"
+        android:layout_marginRight="31dp"
+        android:layout_marginBottom="69dp"
+        android:background="@drawable/bg_add_chemicals_one"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="50px"
+            android:layout_marginTop="40px"
+            android:text="已存储"
+            android:textColor="#333"
+            android:textSize="30px" />
+
+        <ListView
+            android:id="@+id/save_LV"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="50px"
+            tools:listitem="@layout/item_save_list" />
+
+    </LinearLayout>
+</RelativeLayout>

+ 37 - 13
app/src/main/res/layout/activity_unlock.xml

@@ -129,24 +129,48 @@
             android:orientation="horizontal">
 
             <Button
-                android:id="@+id/cancel_BT"
+                android:id="@+id/validate_BT"
                 android:layout_width="250px"
                 android:layout_height="70px"
                 android:layout_marginHorizontal="35px"
                 android:background="@drawable/selector_input_select_bt"
-                android:text="关闭"
+                android:text="认证开门"
                 android:textColor="@color/white" />
 
             <Button
-                android:id="@+id/validate_BT"
+                android:id="@+id/retry_BT"
                 android:layout_width="250px"
                 android:layout_height="70px"
                 android:layout_marginHorizontal="35px"
                 android:background="@drawable/selector_input_select_bt"
-                android:text="认证开门"
-                android:textColor="@color/white" />
+                android:text="批量重试"
+                android:textColor="@color/white"
+                android:visibility="gone" />
+
+            <LinearLayout
+                android:id="@+id/unlock_LL"
+                android:layout_width="250px"
+                android:layout_height="70px"
+                android:background="@drawable/selector_input_select_bt"
+                android:gravity="center"
+                android:orientation="horizontal"
+                android:visibility="gone">
+
+                <ProgressBar
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_vertical"
+                    android:text="开锁中..."
+                    android:textColor="@color/white" />
+
+            </LinearLayout>
 
             <Button
+                android:id="@+id/save_BT"
                 android:layout_width="250px"
                 android:layout_height="70px"
                 android:layout_marginHorizontal="35px"
@@ -169,13 +193,13 @@
                 android:orientation="vertical">
 
                 <LinearLayout
-                    android:layout_width="wrap_content"
+                    android:layout_width="360px"
                     android:layout_height="wrap_content"
                     android:layout_gravity="center_horizontal"
                     android:orientation="vertical">
 
                     <TextView
-                        android:layout_width="340px"
+                        android:layout_width="match_parent"
                         android:layout_height="80px"
                         android:background="#2697FF"
                         android:gravity="center"
@@ -198,9 +222,9 @@
 
                         <TextView
                             android:id="@+id/admin_TV"
-                            android:layout_width="match_parent"
+                            android:layout_width="wrap_content"
                             android:layout_height="match_parent"
-                            android:gravity="center"
+                            android:gravity="center_vertical"
                             android:textSize="18px" />
                     </LinearLayout>
 
@@ -219,9 +243,9 @@
 
                         <TextView
                             android:id="@+id/safety_TV"
-                            android:layout_width="match_parent"
+                            android:layout_width="wrap_content"
                             android:layout_height="match_parent"
-                            android:gravity="center"
+                            android:gravity="center_vertical"
                             android:textSize="18px" />
                     </LinearLayout>
 
@@ -240,9 +264,9 @@
 
                         <TextView
                             android:id="@+id/director_TV"
-                            android:layout_width="match_parent"
+                            android:layout_width="wrap_content"
                             android:layout_height="match_parent"
-                            android:gravity="center"
+                            android:gravity="center_vertical"
                             android:textSize="18px" />
                     </LinearLayout>
 

+ 13 - 0
app/src/main/res/layout/fragment_card_detect.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true"
+        android:src="@mipmap/img_sfrz_sksyt" />
+</RelativeLayout>

+ 55 - 63
app/src/main/res/layout/item_door_unlock.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
@@ -37,92 +38,83 @@
                 android:layout_height="match_parent"
                 android:layout_marginStart="10px"
                 android:layout_marginBottom="5px"
-                android:orientation="horizontal">
+                android:orientation="vertical">
 
                 <LinearLayout
-                    android:layout_width="150px"
+                    android:id="@+id/up_door_unlock_LL"
+                    android:layout_width="match_parent"
                     android:layout_height="match_parent"
-                    android:orientation="vertical">
+                    android:layout_weight="1"
+                    android:gravity="center_vertical"
+                    android:orientation="horizontal">
 
+                    <ImageView
+                        android:id="@+id/up_door_name_IV"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:background="@mipmap/icon_gzxx_zh" />
 
                     <TextView
                         android:id="@+id/up_door_name_TV"
                         android:layout_width="150px"
                         android:layout_height="match_parent"
-                        android:layout_weight="1"
                         android:gravity="center_vertical"
                         android:maxLines="2"
                         android:text="名称"
-                        android:textSize="28px" />
+                        android:textColor="#ff333333"
+                        android:textSize="20px" />
 
-                    <TextView
-                        android:id="@+id/down_door_name_TV"
-                        android:layout_width="150px"
-                        android:layout_height="match_parent"
-                        android:layout_weight="1"
-                        android:gravity="center_vertical"
-                        android:maxLines="2"
-                        android:text="名称"
-                        android:textSize="28px"
+                    <Button
+                        android:id="@+id/up_door_unlock_BT"
+                        android:layout_width="wrap_content"
+                        android:layout_height="45px"
+                        android:layout_gravity="end|center_vertical"
+                        android:layout_margin="5px"
+                        android:background="@drawable/selector_input_bt"
+                        android:gravity="center"
+                        android:text="开门"
+                        android:textColor="@color/white"
+                        android:textSize="20px"
                         android:visibility="gone" />
-
                 </LinearLayout>
 
                 <LinearLayout
-                    android:id="@+id/lock_LL"
-                    android:layout_width="wrap_content"
+                    android:id="@+id/down_door_unlock_LL"
+                    android:layout_width="match_parent"
                     android:layout_height="match_parent"
-                    android:gravity="center"
-                    android:orientation="vertical">
+                    android:layout_weight="1"
+                    android:gravity="center_vertical"
+                    android:orientation="horizontal"
+                    android:visibility="gone">
 
-                    <ProgressBar
-                        android:id="@+id/unlocking_PB"
-                        android:layout_width="match_parent"
-                        android:layout_height="match_parent"
-                        android:layout_centerInParent="true"
-                        android:visibility="gone" />
+                    <ImageView
+                        android:id="@+id/down_door_name_IV"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:background="@mipmap/icon_gzxx_zh" />
 
-                    <LinearLayout
-                        android:id="@+id/up_door_unlock_LL"
-                        android:layout_width="match_parent"
+                    <TextView
+                        android:id="@+id/down_door_name_TV"
+                        android:layout_width="150px"
                         android:layout_height="match_parent"
-                        android:layout_weight="1"
-                        android:visibility="gone">
-
-                        <Button
-                            android:id="@+id/up_door_unlock_BT"
-                            android:layout_width="100px"
-                            android:layout_height="45px"
-                            android:layout_gravity="end|center_vertical"
-                            android:layout_margin="5px"
-                            android:layout_weight="1"
-                            android:background="@drawable/selector_input_bt"
-                            android:gravity="center"
-                            android:text="开门"
-                            android:textColor="@color/white"
-                            android:textSize="20px" />
-                    </LinearLayout>
+                        android:gravity="center_vertical"
+                        android:maxLines="2"
+                        android:text="名称"
+                        android:textColor="#ff333333"
+                        android:textSize="20px" />
 
-                    <LinearLayout
-                        android:id="@+id/down_door_unlock_LL"
+                    <Button
+                        android:id="@+id/down_door_unlock_BT"
                         android:layout_width="wrap_content"
-                        android:layout_height="match_parent"
-                        android:layout_weight="1"
-                        android:visibility="gone">
-
-                        <Button
-                            android:id="@+id/down_door_unlock_BT"
-                            android:layout_width="100px"
-                            android:layout_height="45px"
-                            android:layout_gravity="end|center_vertical"
-                            android:layout_margin="5px"
-                            android:layout_weight="1"
-                            android:background="@drawable/selector_input_bt"
-                            android:gravity="center"
-                            android:text="开门"
-                            android:textColor="@color/white"
-                            android:textSize="20px" />
-                    </LinearLayout>
+                        android:layout_height="45px"
+                        android:layout_gravity="end|center_vertical"
+                        android:layout_margin="5px"
+                        android:background="@drawable/selector_input_bt"
+                        android:gravity="center"
+                        android:text="开门"
+                        android:textColor="@color/white"
+                        android:textSize="20px"
+                        android:visibility="gone" />
                 </LinearLayout>
             </LinearLayout>
         </LinearLayout>

+ 258 - 0
app/src/main/res/layout/item_save_list.xml

@@ -0,0 +1,258 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    tools:ignore="PxUsage">
+
+    <LinearLayout
+        android:id="@+id/titlle_LL"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:visibility="gone">
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginVertical="10px">
+
+            <TextView
+                android:id="@+id/cabinet_TV"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:text="柜子名-柜门名"
+                android:textColor="#333"
+                android:textSize="30px" />
+
+            <Button
+                android:id="@+id/unlock_BT"
+                android:layout_width="150px"
+                android:layout_height="50px"
+                android:layout_centerVertical="true"
+                android:layout_marginStart="40px"
+                android:layout_toEndOf="@id/cabinet_TV"
+                android:background="@drawable/selector_input_bt"
+                android:text="再次开门"
+                android:textColor="@color/white"
+                android:textSize="26px" />
+
+            <TextView
+                android:id="@+id/sum_TV"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_centerVertical="true"
+                android:layout_gravity="end"
+                android:layout_marginEnd="40px"
+                android:text="数量:3"
+                android:textColor="#333" />
+        </RelativeLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="80px"
+            android:background="#F5F5F5">
+
+            <TextView
+                android:layout_width="215px"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                android:paddingStart="30px"
+                android:text="名称"
+                android:textColor="#333"
+                android:textSize="26px"
+                android:textStyle="bold" />
+
+            <TextView
+                android:layout_width="180px"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                android:paddingStart="20px"
+                android:text="CAS"
+                android:textColor="#333"
+                android:textSize="26px"
+                android:textStyle="bold" />
+
+            <TextView
+                android:layout_width="165px"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                android:paddingStart="20px"
+                android:text="类别"
+                android:textColor="#333"
+                android:textSize="26px"
+                android:textStyle="bold" />
+
+            <TextView
+                android:layout_width="165px"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                android:paddingStart="20px"
+                android:text="级别"
+                android:textColor="#333"
+                android:textSize="26px"
+                android:textStyle="bold" />
+
+            <TextView
+                android:layout_width="165px"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                android:paddingStart="20px"
+                android:text="规格"
+                android:textColor="#333"
+                android:textSize="26px"
+                android:textStyle="bold" />
+
+            <TextView
+                android:layout_width="165px"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:text="净含量"
+                android:textColor="#333"
+                android:textSize="26px"
+                android:textStyle="bold" />
+
+            <TextView
+                android:layout_width="165px"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:text="数量"
+                android:textColor="#333"
+                android:textSize="26px"
+                android:textStyle="bold" />
+
+            <TextView
+                android:layout_width="165px"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:text="归属人"
+                android:textColor="#333"
+                android:textSize="26px"
+                android:textStyle="bold" />
+
+            <TextView
+                android:layout_width="165px"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:text="是否全新"
+                android:textColor="#333"
+                android:textSize="26px"
+                android:textStyle="bold" />
+
+            <TextView
+                android:layout_width="165px"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:text="存储层"
+                android:textColor="#333"
+                android:textSize="26px"
+                android:textStyle="bold" />
+
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="80px">
+
+        <TextView
+            android:id="@+id/name"
+            android:layout_width="215px"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical"
+            android:paddingStart="30px"
+            android:text="名称"
+            android:textColor="#333"
+            android:textSize="20px" />
+
+        <TextView
+            android:id="@+id/cas"
+            android:layout_width="180px"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical"
+            android:paddingStart="20px"
+            android:text="CAS"
+            android:textColor="#333"
+            android:textSize="20px" />
+
+        <TextView
+            android:id="@+id/type"
+            android:layout_width="165px"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical"
+            android:paddingStart="20px"
+            android:text="类别"
+            android:textColor="#333"
+            android:textSize="20px" />
+
+        <TextView
+            android:id="@+id/level"
+            android:layout_width="165px"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical"
+            android:paddingStart="20px"
+            android:text="级别"
+            android:textColor="#333"
+            android:textSize="20px" />
+
+        <TextView
+            android:id="@+id/specs"
+            android:layout_width="165px"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical"
+            android:paddingStart="20px"
+            android:text="规格"
+            android:textColor="#333"
+            android:textSize="20px" />
+
+        <TextView
+            android:id="@+id/netWT"
+            android:layout_width="165px"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:text="净含量"
+            android:textColor="#333"
+            android:textSize="20px" />
+
+        <TextView
+            android:id="@+id/hxp_sum"
+            android:layout_width="165px"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:text="数量"
+            android:textColor="#333"
+            android:textSize="20px" />
+
+        <TextView
+            android:id="@+id/owner"
+            android:layout_width="165px"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:text="归属人"
+            android:textColor="#333"
+            android:textSize="20px" />
+
+        <TextView
+            android:id="@+id/new_TV"
+            android:layout_width="165px"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:text="是否全新"
+            android:textColor="#333"
+            android:textSize="20px" />
+
+        <TextView
+            android:id="@+id/layer"
+            android:layout_width="165px"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:text="存储层"
+            android:textColor="#333"
+            android:textSize="20px" />
+
+    </LinearLayout>
+
+</LinearLayout>

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


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


+ 0 - 0
app/src/main/res/mipmap-xhdpi/icon_gzxx_zh.png


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


+ 1 - 1
app/src/main/res/values/themes.xml

@@ -18,7 +18,7 @@
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowFullscreen">true</item>
         <item name="android:windowIsTranslucent">true</item>
-        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowBackground">@color/white</item>
     </style>
 
 </resources>