Kaynağa Gözat

1.新增二维码准入
2.新增规章制度
3.完善除监控外所有功能

JaycePC 1 hafta önce
ebeveyn
işleme
e4502f0627
61 değiştirilmiş dosya ile 2459 ekleme ve 626 silme
  1. 5 0
      app/build.gradle
  2. 15 1
      app/src/main/AndroidManifest.xml
  3. 4 0
      app/src/main/java/core/ui/activity/BaseActivity.java
  4. 1 2
      app/src/main/java/core/ui/activity/BaseCountDownActivity.java
  5. 5 5
      app/src/main/java/core/ui/activity/BaseSignActivity.kt
  6. 7 1
      app/src/main/java/core/ui/common/UIDelegateImpl.kt
  7. 2 2
      app/src/main/java/http/OkHttpUtils.java
  8. 2 8
      app/src/main/java/http/client/HttpTool.java
  9. 3 3
      app/src/main/java/http/client/retrofit/ApiService.java
  10. 256 0
      app/src/main/java/http/net/DownloadHelper.java
  11. 0 156
      app/src/main/java/http/net/DownloadTask.kt
  12. 57 1
      app/src/main/java/http/vo/response/SafeClassify.java
  13. 42 7
      app/src/main/java/xn/xxp/HomeActivity.java
  14. 15 37
      app/src/main/java/xn/xxp/HomeActivityHelp.java
  15. 1 0
      app/src/main/java/xn/xxp/app/InitActivity.java
  16. 1 0
      app/src/main/java/xn/xxp/app/LabApp.java
  17. 37 14
      app/src/main/java/xn/xxp/app/SyncFaceTool.java
  18. 2 3
      app/src/main/java/xn/xxp/home/AccessVerify.kt
  19. 106 0
      app/src/main/java/xn/xxp/home/adapter/PeopleFlipperAdapter.java
  20. 0 90
      app/src/main/java/xn/xxp/home/adapter/PersonFlipperAdapter.kt
  21. 7 5
      app/src/main/java/xn/xxp/home/auth/ChoiceAuthActivity.java
  22. 2 1
      app/src/main/java/xn/xxp/home/auth/OnAuthResultListener.kt
  23. 86 60
      app/src/main/java/xn/xxp/home/auth/fragment/LzFaceAuthFragment.java
  24. 64 23
      app/src/main/java/xn/xxp/home/auth/fragment/QrAuthFragment.java
  25. 1 0
      app/src/main/java/xn/xxp/home/fragment/LabBasicFragment.kt
  26. 2 1
      app/src/main/java/xn/xxp/home/sign/SafetyCheckFragment.kt
  27. 1 0
      app/src/main/java/xn/xxp/home/window/WindowHorizontalAdapter.java
  28. 1 1
      app/src/main/java/xn/xxp/home/window/WindowTextlAdapter.java
  29. 8 11
      app/src/main/java/xn/xxp/home/window/WindowVerticalAdapter.java
  30. 11 9
      app/src/main/java/xn/xxp/main/MainActivity.kt
  31. 1 1
      app/src/main/java/xn/xxp/main/msds/HtmlFullScreenActivity.kt
  32. 1 1
      app/src/main/java/xn/xxp/main/msds/InstructionActivity.kt
  33. 12 0
      app/src/main/java/xn/xxp/main/risk/PlayVideoEvent.java
  34. 0 176
      app/src/main/java/xn/xxp/main/risk/RiskActivity.kt
  35. 386 0
      app/src/main/java/xn/xxp/main/risk/RiskListActivity.java
  36. 42 0
      app/src/main/java/xn/xxp/main/risk/RiskListAdapter.java
  37. 263 0
      app/src/main/java/xn/xxp/main/risk/VideoDialogFragment.java
  38. 13 0
      app/src/main/java/xn/xxp/main/risk/WebAppInterface.java
  39. 170 0
      app/src/main/java/xn/xxp/main/rule/RuleActivity.java
  40. 61 0
      app/src/main/java/xn/xxp/main/rule/RuleAdapter.java
  41. 167 0
      app/src/main/java/xn/xxp/main/rule/RuleDetailActivity.java
  42. 1 0
      app/src/main/java/xn/xxp/mqtt/MqttManager.kt
  43. 17 0
      app/src/main/java/xn/xxp/receiver/TimeTickReceiver.java
  44. 1 1
      app/src/main/java/xn/xxp/room/bean/LabConfig.java
  45. 1 1
      app/src/main/java/xn/xxp/utils/Constants.kt
  46. 4 0
      app/src/main/java/xn/xxp/utils/Tool.java
  47. 7 0
      app/src/main/res/anim/slide_in_bottom.xml
  48. 7 0
      app/src/main/res/anim/slide_out_bottom.xml
  49. 11 0
      app/src/main/res/layout/activity_browser.xml
  50. 1 2
      app/src/main/res/layout/activity_main.xml
  51. 3 1
      app/src/main/res/layout/activity_risk.xml
  52. 189 0
      app/src/main/res/layout/activity_risk_list.xml
  53. 133 0
      app/src/main/res/layout/activity_rule.xml
  54. 162 0
      app/src/main/res/layout/activity_rule_detail.xml
  55. 12 0
      app/src/main/res/layout/dialog_video.xml
  56. 29 0
      app/src/main/res/layout/item_rule.xml
  57. 0 1
      app/src/main/res/layout/item_safe_classify.xml
  58. BIN
      app/src/main/res/mipmap-xhdpi/icon_sy_hkm.webp
  59. BIN
      app/src/main/res/mipmap-xhdpi/img_bg_hkclb.webp
  60. 15 0
      app/src/main/res/values/styles.xml
  61. 6 1
      settings.gradle

+ 5 - 0
app/build.gradle

@@ -119,5 +119,10 @@ dependencies {
     // fotoapparat
     implementation libs.fotoapparat
 
+    implementation 'com.shuyu:gsyVideoPlayer-java:8.1.0'
+    implementation 'com.shuyu:gsyVideoPlayer-armv7a:8.1.0'
 
+    implementation 'org.jsoup:jsoup:1.16.2'
+
+    implementation 'com.github.barteksc:android-pdf-viewer:3.2.0-beta.1'
 }

+ 15 - 1
app/src/main/AndroidManifest.xml

@@ -16,6 +16,7 @@
     <uses-feature android:name="android.hardware.camera.any" />
 
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
 
@@ -24,6 +25,7 @@
         android:allowBackup="true"
         android:dataExtractionRules="@xml/data_extraction_rules"
         android:fullBackupContent="@xml/backup_rules"
+        android:hardwareAccelerated="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
         android:networkSecurityConfig="@xml/network_security_config"
@@ -32,6 +34,18 @@
         android:supportsRtl="true"
         android:theme="@style/Theme.西农电子信息牌"
         android:usesCleartextTraffic="true">
+        <activity
+            android:name=".main.rule.RuleDetailActivity"
+            android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
+            android:exported="false" />
+        <activity
+            android:name=".main.rule.RuleActivity"
+            android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
+            android:exported="false" />
+        <activity
+            android:name=".home.notice.NoticeMsgSystemActivity"
+            android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
+            android:exported="false" />
         <activity
             android:name=".main.msds.HtmlFullScreenActivity"
             android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
@@ -45,7 +59,7 @@
             android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
             android:exported="false" />
         <activity
-            android:name=".main.risk.RiskActivity"
+            android:name=".main.risk.RiskListActivity"
             android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
             android:exported="false" />
         <activity

+ 4 - 0
app/src/main/java/core/ui/activity/BaseActivity.java

@@ -7,6 +7,7 @@ import android.view.View;
 
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.RecyclerView;
 import androidx.viewbinding.ViewBinding;
 
 import com.blankj.utilcode.util.LogUtils;
@@ -92,4 +93,7 @@ public abstract class BaseActivity<VB extends ViewBinding> extends AppCompatActi
         uiDelegate.removeDisposable(disposable);
     }
 
+    public RecyclerView.ItemDecoration createItemDecoration() {
+        return uiDelegate.createItemDecoration(this);
+    }
 }

+ 1 - 2
app/src/main/java/core/ui/activity/BaseCountDownActivity.java

@@ -10,11 +10,10 @@ import com.blankj.utilcode.util.LogUtils;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.Consumer;
 
 import xn.xxp.HomeActivity;
-import xn.xxp.home.notice.NoticeListActivity;
 import xn.xxp.app.LabApp;
+import xn.xxp.home.notice.NoticeListActivity;
 import xn.xxp.room.RoomTool;
 import xn.xxp.room.bean.NoticeSummary;
 import xn.xxp.room.dao.NoticeSummaryDao;

+ 5 - 5
app/src/main/java/core/ui/activity/BaseSignActivity.kt

@@ -2,7 +2,9 @@ package core.ui.activity
 
 import android.content.Intent
 import android.os.Bundle
+import android.util.Log
 import androidx.viewbinding.ViewBinding
+import com.blankj.utilcode.util.LogUtils
 import core.util.ifNullOrEmpty
 import http.client.ApiRepository
 import http.exception.NetException
@@ -168,18 +170,16 @@ abstract class BaseSignActivity<VB : ViewBinding> : BaseCountDownActivity<VB>()
                     this,
                     ResultEnum.SUCCESS,
                     "签到成功",
-                    3000
+                    1500
                 ).apply {
                     setCancelable(false)
                     setOnDismissListener { onSignInFinish(true) }
                     show()
                 }
             }, { throwable ->
+                LogUtils.e(Log.getStackTraceString(throwable))
                 dismissLoading()
-                if (!pretreatmentError(throwable, "签到")) {
-                    throwable.printStackTrace()
-                    showNetError(throwable)
-                }
+                showNetError(throwable)
             })
         addDisposable(disposable)
     }

+ 7 - 1
app/src/main/java/core/ui/common/UIDelegateImpl.kt

@@ -1,11 +1,13 @@
 package core.ui.common
 
 import android.content.Context
+import android.util.Log
 import android.widget.Toast
 import androidx.core.content.ContextCompat
 import androidx.recyclerview.widget.DividerItemDecoration
 import androidx.recyclerview.widget.RecyclerView
 import com.blankj.utilcode.util.ActivityUtils
+import com.blankj.utilcode.util.LogUtils
 import com.blankj.utilcode.util.ThreadUtils
 import com.kongzue.dialogx.dialogs.WaitDialog
 import http.exception.NetException
@@ -34,7 +36,11 @@ class UIDelegateImpl : AbsUIDelegate() {
 
     override fun showToast(context: Context?, message: String) {
         ThreadUtils.runOnUiThread {
-            Toast.makeText(ActivityUtils.getTopActivity(), message, Toast.LENGTH_SHORT).show()
+            try {
+                Toast.makeText(ActivityUtils.getTopActivity(), message, Toast.LENGTH_SHORT).show()
+            } catch (e: Exception) {
+                LogUtils.e(Log.getStackTraceString(e))
+            }
         }
     }
 

+ 2 - 2
app/src/main/java/http/OkHttpUtils.java

@@ -11,8 +11,8 @@ import okhttp3.Response;
 
 public class OkHttpUtils {
 
-    private static final OkHttpClient client = HttpClient.buildHttpClient();
-    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+    public static final OkHttpClient client = HttpClient.buildHttpClient();
+    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
 
     private OkHttpUtils() {
     }

+ 2 - 8
app/src/main/java/http/client/HttpTool.java

@@ -12,8 +12,6 @@ import http.vo.request.TerminalAuthReq;
 import org.json.JSONObject;
 
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
 
 import okhttp3.Response;
 import xn.xxp.room.RoomTool;
@@ -117,12 +115,8 @@ public final class HttpTool {
      *
      * @param code 时间
      */
-    public static Response infobordLogin(String devId, String code) throws IOException {
-        Map<String, Object> map = new HashMap<>();
-        map.put("core", code);
-        map.put("macId", devId);
-        LogUtils.json(map);
+    public static Response infoBordLogin(String devId, String code) throws IOException {
         DeviceConfig deviceConfig = RoomTool.getInstance().deviceConfigDao().getDeviceConfig();
-        return OkHttpUtils.postSync(deviceConfig.getBaseUrl() + "/app/board/infobordLogin", GsonUtils.toJson(map));
+        return OkHttpUtils.getSync(deviceConfig.getBaseUrl() + "terminal/lab/infobordLogin?macId=" + devId + "&code=" + code);
     }
 }

+ 3 - 3
app/src/main/java/http/client/retrofit/ApiService.java

@@ -97,7 +97,7 @@ public interface ApiService {
      * ZD-A008: 签到提交-人脸验证
      */
     @POST("terminal/lab/onemachine/{code}/SignIn2")
-    Observable<CommonDataResponse<String>> signInFace(@Path("core") String code, @Body SignInReq data);
+    Observable<CommonDataResponse<String>> signInFace(@Path("code") String code, @Body SignInReq data);
 
     /**
      * ZD-A009: 签到-安全准入检测三合一
@@ -123,7 +123,7 @@ public interface ApiService {
      * ZD-A012: 签到提交-有跳过安全准入检测时使用
      */
     @POST("terminal/lab/checklog/{id}/jump/{code}")
-    Observable<CommonResponse> signInJump(@Path("id") String id, @Path("core") String code);
+    Observable<CommonResponse> signInJump(@Path("id") String id, @Path("code") String code);
 
     /**
      * ZD-A013: 签到验证(离开)
@@ -141,7 +141,7 @@ public interface ApiService {
      * ZD-A015: 离开提交
      */
     @POST("terminal/lab/onemachine/{code}/SignOut")
-    Observable<CommonResponse> signOut(@Path("core") String code);
+    Observable<CommonResponse> signOut(@Path("code") String code);
 
     /**
      * ZD-A016: 获取实验室一体机可控制设备

+ 256 - 0
app/src/main/java/http/net/DownloadHelper.java

@@ -0,0 +1,256 @@
+package http.net;
+
+import android.annotation.SuppressLint;
+import android.app.DownloadManager;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.lang.ref.WeakReference;
+import java.util.function.Consumer;
+
+/**
+ * 下载文件
+ *
+ * @author Jayce
+ */
+@SuppressLint("Range")
+public class DownloadHelper {
+    private static final String TAG = "DownloadHelper";
+    private final Context context; // 应用上下文,使用ApplicationContext避免内存泄漏
+    private final DownloadManager downloadManager; // 系统下载管理器实例
+    private final SparseArray<DownloadTask> downloadTasks = new SparseArray<>(); // 存储所有下载任务,key为自定义任务ID
+    private final Handler handler; // 主线程Handler,用于执行回调
+
+    public DownloadHelper(Context context) {
+        this.context = context.getApplicationContext();
+        // 获取系统下载服务
+        this.downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
+        // 创建主线程Handler用于回调
+        this.handler = new Handler(Looper.getMainLooper());
+    }
+
+    // 下载回调接口
+    public interface DownloadCallback {
+        void onProgress(int progress, long downloadedBytes, long totalBytes); // 下载进度更新
+
+        void onSuccess(Uri fileUri);         // 下载成功
+
+        void onFailure(int reason);          // 下载失败(携带系统错误码)
+
+        void onPaused();                     // 下载暂停
+
+        void onCancelled();                  // 下载取消
+    }
+
+    // 下载任务封装类
+    private static class DownloadTask {
+        final long downloadId;               // 系统分配的下载ID
+        final WeakReference<DownloadCallback> callbackRef; // 使用弱引用防止内存泄漏
+        final ContentObserver observer;      // 内容观察者,用于监听下载状态变化
+
+        DownloadTask(long downloadId, DownloadCallback callback, ContentObserver observer) {
+            this.downloadId = downloadId;
+            this.callbackRef = new WeakReference<>(callback);
+            this.observer = observer;
+        }
+    }
+
+    /**
+     * 启动下载任务
+     *
+     * @param url      下载地址
+     * @param fileName 保存文件名
+     * @param taskId   自定义任务ID(用于区分不同任务)
+     * @param callback 下载状态回调
+     */
+    public void startDownload(String url, String fileName, int taskId, DownloadCallback callback) {
+        // 检查任务是否已存在
+        if (downloadTasks.get(taskId) != null) {
+            Log.w(TAG, "Task already exists: " + taskId);
+            return;
+        }
+
+        try {
+            // 创建下载请求
+            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url))
+                    .setTitle(fileName)
+                    .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) // 显示系统通知
+                    .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); // 保存到公共下载目录
+
+            long downloadId = downloadManager.enqueue(request); // 加入下载队列
+            ContentObserver observer = registerObserver(downloadId, callback); // 注册内容观察者
+            downloadTasks.put(taskId, new DownloadTask(downloadId, callback, observer)); // 记录任务信息
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Invalid download URL", e);
+            notifyCallback(callback, true, c -> c.onFailure(DownloadManager.ERROR_UNKNOWN)); // 通知无效URL错误
+        }
+    }
+
+    /**
+     * 取消指定下载任务
+     *
+     * @param taskId 自定义任务ID
+     */
+    public void cancelDownload(int taskId) {
+        DownloadTask task = downloadTasks.get(taskId);
+        if (task != null) {
+            try {
+                downloadManager.remove(task.downloadId); // 从下载管理器移除
+            } catch (IllegalArgumentException e) {
+                Log.w(TAG, "Download already removed: " + task.downloadId);
+            }
+            unregisterObserver(task.observer); // 注销观察者
+            notifyCallback(task.callbackRef, true, DownloadCallback::onCancelled); // 触发取消回调
+            downloadTasks.remove(taskId); // 移除任务记录
+        }
+    }
+
+    /**
+     * 注册内容观察者监听下载状态
+     *
+     * @param downloadId 系统下载ID
+     * @param callback   下载回调
+     * @return 注册的内容观察者实例
+     */
+    private ContentObserver registerObserver(long downloadId, DownloadCallback callback) {
+        // 构建指定下载任务的URI
+        Uri uri = ContentUris.withAppendedId(
+                Uri.parse("content://downloads/file"), downloadId);
+
+        ContentObserver observer = new ContentObserver(handler) {
+            private int lastProgress = -1; // 上次进度,用于避免重复回调
+
+            @Override
+            public void onChange(boolean selfChange) {
+                Cursor cursor = null;
+                try {
+                    // 查询下载状态
+                    cursor = downloadManager.query(new DownloadManager.Query().setFilterById(downloadId));
+                    if (cursor != null && cursor.moveToFirst()) {
+                        int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
+                        long bytesDownloaded = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
+                        long bytesTotal = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
+
+                        // 根据状态分发处理
+                        switch (status) {
+                            case DownloadManager.STATUS_RUNNING:
+                                handleProgress(bytesDownloaded, bytesTotal);
+                                break;
+                            case DownloadManager.STATUS_SUCCESSFUL:
+                                handleSuccess();
+                                break;
+                            case DownloadManager.STATUS_FAILED:
+                                handleFailure(cursor);
+                                break;
+                            case DownloadManager.STATUS_PAUSED:
+                                handlePaused();
+                                break;
+                        }
+                    }
+                } catch (Exception e) {
+                    Log.e(TAG, "Error processing download update", e);
+                } finally {
+                    if (cursor != null && !cursor.isClosed()) {
+                        cursor.close(); // 确保关闭Cursor
+                    }
+                }
+            }
+
+            // 处理下载进度更新
+            private void handleProgress(long downloaded, long total) {
+                int progress = total > 0 ? (int) ((downloaded * 100L) / total) : 0;
+                if (progress != lastProgress) { // 避免相同进度重复回调
+                    lastProgress = progress;
+                    notifyCallback(callback, false, c -> c.onProgress(progress, downloaded, total));
+                }
+            }
+
+            // 处理下载成功
+            private void handleSuccess() {
+                try {
+                    Uri uri = downloadManager.getUriForDownloadedFile(downloadId);
+                    notifyCallback(callback, true, c -> c.onSuccess(uri));
+                } catch (SecurityException e) {
+                    Log.e(TAG, "File access permission denied", e);
+                    notifyCallback(callback, true, c -> c.onFailure(DownloadManager.ERROR_FILE_ERROR));
+                } finally {
+                    unregisterObserver(this); // 无论成功失败都注销观察者
+                }
+            }
+
+            // 处理下载失败
+            private void handleFailure(Cursor cursor) {
+                int reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON));
+                notifyCallback(callback, true, c -> c.onFailure(reason));
+                unregisterObserver(this);
+            }
+
+            // 处理下载暂停
+            private void handlePaused() {
+                notifyCallback(callback, false, DownloadCallback::onPaused);
+            }
+        };
+
+        context.getContentResolver().registerContentObserver(uri, true, observer);
+        return observer;
+    }
+
+    // 注销内容观察者
+    private void unregisterObserver(ContentObserver observer) {
+        try {
+            context.getContentResolver().unregisterContentObserver(observer);
+        } catch (Exception e) {
+            Log.w(TAG, "Error unregistering observer", e);
+        }
+    }
+
+    /**
+     * 安全执行回调(主线程)
+     *
+     * @param callback      回调实例
+     * @param finalCallback 是否最终回调(触发后清除引用)
+     * @param action        要执行的回调操作
+     */
+    private void notifyCallback(DownloadCallback callback, boolean finalCallback,
+                                Consumer<DownloadCallback> action) {
+        notifyCallback(new WeakReference<>(callback), finalCallback, action);
+    }
+
+    // 带弱引用的回调方法
+    private void notifyCallback(WeakReference<DownloadCallback> ref, boolean finalCallback,
+                                Consumer<DownloadCallback> action) {
+        handler.post(() -> {
+            DownloadCallback callback = ref.get();
+            if (callback != null) {
+                try {
+                    action.accept(callback); // 执行回调
+                } catch (Exception e) {
+                    Log.e(TAG, "Callback execution error", e);
+                }
+            }
+            if (finalCallback) {
+                ref.clear(); // 清除弱引用
+            }
+        });
+    }
+
+    /**
+     * 释放所有资源(建议在不再需要时调用)
+     */
+    public void release() {
+        for (int i = 0; i < downloadTasks.size(); i++) {
+            DownloadTask task = downloadTasks.valueAt(i);
+            unregisterObserver(task.observer); // 注销所有观察者
+            notifyCallback(task.callbackRef, true, DownloadCallback::onCancelled); // 通知取消
+        }
+        downloadTasks.clear(); // 清空任务列表
+    }
+}

+ 0 - 156
app/src/main/java/http/net/DownloadTask.kt

@@ -1,156 +0,0 @@
-package http.net
-
-import android.os.AsyncTask
-import android.os.Environment
-import android.text.TextUtils
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import java.io.File
-import java.io.InputStream
-import java.io.RandomAccessFile
-
-/**
- * eg: http://www.download.com/projectname/dl/test.apk
- * 将文件下载到sd卡/Download目录。
- * 下载到本地的文件件名,如果没指定则为test.apk。
- *
- */
-class DownloadTask(
-    private val listener: DownloadListener,
-    private val dlPath: String = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path,
-    private var fileName: String?,
-    private val headers: Map<String, String>? = null
-) :
-    AsyncTask<String, Int, Int>() {
-
-    companion object {
-        const val TYPE_SUCCESS: Int = 1
-        const val TYPE_FAILED: Int = 2
-        const val TYPE_PAUSED: Int = 3
-        const val TYPE_CANCELED: Int = 4
-    }
-
-    private var isCanceled: Boolean = false
-    private var isPaused: Boolean = false
-
-    private var lastProgress: Int = 0
-
-    override fun doInBackground(vararg params: String?): Int {
-        var file: File? = null
-        var inputStream: InputStream? = null
-        var saveFile: RandomAccessFile? = null
-        try {
-            var downloadedLength = 0L
-            val downloadUrl = params[0]
-            downloadUrl ?: return TYPE_FAILED
-
-            if (TextUtils.isEmpty(fileName)) {
-                fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"))
-            }
-
-            file = File(dlPath, fileName!!)
-            if (file.exists()) {
-                downloadedLength = file.length()
-            }
-            val contentLength: Long = getContentLength(downloadUrl)
-            if (contentLength == 0L) {
-                return TYPE_FAILED
-            } else if (contentLength == downloadedLength) {
-                // 已经下载的字节数和文件的总字节数相等,说明已经下载完成。
-                return TYPE_SUCCESS
-            }
-            val builder = Request.Builder()
-            headers?.let {
-                for ((key, value) in it)
-                    builder.addHeader(key, value)
-            }
-            val request = builder
-                // 断点下载,指定从那个字节开始下
-                .addHeader("RANGE", "bytes=" + downloadedLength + "-")
-                .url(downloadUrl)
-                .build()
-            val response = OkHttpClient()
-                .newCall(request)
-                .execute()
-
-            inputStream = response.body!!.byteStream()
-            saveFile = RandomAccessFile(file, "rw")
-            saveFile.seek(downloadedLength) // 跳过已经下载的字节
-            val buff = ByteArray(1024)
-            var total = 0
-            var len = 0;
-            while ({ len = inputStream.read(buff);len }() != -1) {
-                if (isCanceled) {
-                    return TYPE_CANCELED
-                } else if (isPaused) {
-                    return TYPE_PAUSED
-                } else {
-                    total += len
-                    saveFile.write(buff, 0, len)
-                    // 计算已经下载的百分比
-                    val progress: Int = ((total + downloadedLength) * 100 / contentLength).toInt()
-                    publishProgress(progress)
-                }
-            }
-            response.body!!.close()
-            return TYPE_SUCCESS
-        } catch (e: Exception) {
-            e.printStackTrace()
-        } finally {
-            try {
-                inputStream?.close()
-                saveFile?.close()
-                if (isCanceled) file?.delete()
-            } catch (e: Exception) {
-                e.printStackTrace()
-            }
-        }
-        return TYPE_FAILED
-    }
-
-    override fun onProgressUpdate(vararg values: Int?) {
-        val progress = values[0]
-        progress?.let {
-            if (it > lastProgress) {
-                listener.onProgress(it)
-                lastProgress = it
-            }
-        }
-    }
-
-    override fun onPostExecute(status: Int?) {
-        when (status) {
-            TYPE_SUCCESS -> listener.onSuccess()
-            TYPE_FAILED -> listener.onFailed(null)
-            TYPE_PAUSED -> listener.onPaused()
-            TYPE_CANCELED -> listener.onCanceled()
-            else -> Unit
-        }
-    }
-
-    fun pauseDownload() {
-        this.isPaused = true
-    }
-
-    fun cancelDownload() {
-        this.isCanceled = true
-    }
-
-    private fun getContentLength(downloadUrl: String): Long {
-        val builder = Request.Builder()
-        headers?.let {
-            for ((key, value) in it)
-                builder.addHeader(key, value)
-        }
-        val request = builder.url(downloadUrl).build()
-        val response = OkHttpClient()
-            .newCall(request)
-            .execute()
-        if (response.isSuccessful) {
-            val contentLength = response.body!!.contentLength()
-            response.close()
-            return contentLength
-        }
-        return 0
-    }
-}

+ 57 - 1
app/src/main/java/http/vo/response/SafeClassify.java

@@ -1,5 +1,6 @@
 package http.vo.response;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -15,6 +16,61 @@ public class SafeClassify {
     public String isSpecial; // 是否为特殊标记 1-否 2-是
     public String isShow;
     public String showColour; // 头部背景色 #B88B7D
-    public List<String> classifyList; // 分类内容
+    public List<String> classifyList = new ArrayList<>(); // 分类内容
 
+    public String getClassifyName() {
+        return classifyName;
+    }
+
+    public void setClassifyName(String classifyName) {
+        this.classifyName = classifyName;
+    }
+
+    public int getClassifyType() {
+        return classifyType;
+    }
+
+    public void setClassifyType(int classifyType) {
+        this.classifyType = classifyType;
+    }
+
+    public String getSort() {
+        return sort;
+    }
+
+    public void setSort(String sort) {
+        this.sort = sort;
+    }
+
+    public String getIsSpecial() {
+        return isSpecial;
+    }
+
+    public void setIsSpecial(String isSpecial) {
+        this.isSpecial = isSpecial;
+    }
+
+    public String getIsShow() {
+        return isShow;
+    }
+
+    public void setIsShow(String isShow) {
+        this.isShow = isShow;
+    }
+
+    public String getShowColour() {
+        return showColour;
+    }
+
+    public void setShowColour(String showColour) {
+        this.showColour = showColour;
+    }
+
+    public List<String> getClassifyList() {
+        return classifyList;
+    }
+
+    public void setClassifyList(List<String> classifyList) {
+        this.classifyList = classifyList;
+    }
 }

+ 42 - 7
app/src/main/java/xn/xxp/HomeActivity.java

@@ -7,7 +7,6 @@ import android.os.Bundle;
 import android.os.CountDownTimer;
 import android.text.TextUtils;
 import android.view.View;
-import android.widget.Toast;
 
 import androidx.core.content.ContextCompat;
 import androidx.fragment.app.Fragment;
@@ -25,22 +24,26 @@ import com.kongzue.dialogx.dialogs.InputDialog;
 import org.greenrobot.eventbus.Subscribe;
 import org.greenrobot.eventbus.ThreadMode;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
 import core.ui.activity.BaseCountDownActivity;
 import core.ui.widget.decoration.NoLastLineItemDecoration;
 import core.util.FastClickDelegate;
+import http.vo.response.LabPersonVo;
 import http.vo.response.LabWarnVo;
 import kotlin.Unit;
 import kotlin.jvm.functions.Function1;
 import xn.xxp.app.InitActivity;
 import xn.xxp.app.SettingActivity;
+import xn.xxp.app.SyncFaceTool;
 import xn.xxp.databinding.ActivityHomeBinding;
 import xn.xxp.home.AccessVerify;
 import xn.xxp.home.AuthTypeConfig;
 import xn.xxp.home.HomeFragmentCallback;
 import xn.xxp.home.adapter.HomeBoardAdapter;
+import xn.xxp.home.adapter.PeopleFlipperAdapter;
 import xn.xxp.home.auth.ChoiceAuthActivity;
 import xn.xxp.home.fragment.AccessPersonFragment;
 import xn.xxp.home.fragment.DutyPersonFragment;
@@ -75,6 +78,12 @@ public class HomeActivity extends BaseCountDownActivity<ActivityHomeBinding> imp
     private FragmentManager fragmentManager;
     private CountDownTimer defaultCd;
     private int defaultCdCount = 0;
+    protected PeopleFlipperAdapter personFlipperAdapter;
+    protected PeopleFlipperAdapter experimentAdapter;
+    protected PeopleFlipperAdapter accessAdapter;
+    protected List<LabPersonVo> personList = new ArrayList<>();
+    protected List<LabPersonVo> experimentList = new ArrayList<>();
+    protected List<LabPersonVo> accessList = new ArrayList<>();
 
     @Override
     protected ActivityHomeBinding createViewBinding() {
@@ -84,6 +93,29 @@ public class HomeActivity extends BaseCountDownActivity<ActivityHomeBinding> imp
     @Override
     protected void initViews(Bundle savedInstanceState) {
         super.initViews(savedInstanceState);
+
+        // 值班人员
+        personFlipperAdapter = new PeopleFlipperAdapter(1, personList);
+        binding.dutyPerson.setAdapter(personFlipperAdapter);
+        binding.dutyPerson.setFlipInterval(3000);
+        binding.dutyPerson.setEmptyView(binding.dutyPersonEmpty);
+        binding.dutyPerson.setInAnimation(this, R.animator.anim_in);
+        binding.dutyPerson.setOutAnimation(this, R.animator.anim_out);
+        // 实验人员
+        experimentAdapter = new PeopleFlipperAdapter(2, experimentList);
+        binding.experimentPerson.setAdapter(experimentAdapter);
+        binding.experimentPerson.setFlipInterval(3000);
+        binding.experimentPerson.setEmptyView(binding.experimentPersonEmpty);
+        binding.experimentPerson.setInAnimation(this, R.animator.anim_in);
+        binding.experimentPerson.setOutAnimation(this, R.animator.anim_out);
+        // 准入人员
+        accessAdapter = new PeopleFlipperAdapter(3, accessList);
+        binding.accessPerson.setAdapter(accessAdapter);
+        binding.accessPerson.setFlipInterval(3000);
+        binding.accessPerson.setEmptyView(binding.accessPersonEmpty);
+        binding.accessPerson.setInAnimation(this, R.animator.anim_in);
+        binding.accessPerson.setOutAnimation(this, R.animator.anim_out);
+
         deviceConfigDao = RoomTool.getInstance().deviceConfigDao();
         labConfigDao = RoomTool.getInstance().labConfigDao();
         deviceConfig = deviceConfigDao.getDeviceConfig();
@@ -107,11 +139,6 @@ public class HomeActivity extends BaseCountDownActivity<ActivityHomeBinding> imp
         homeBoardAdapter = new HomeBoardAdapter();
         binding.bulletinBoardView.setAdapter(homeBoardAdapter);
 
-        // 接口业务
-        help.queryBulletinBoard();
-        help.queryPerson();
-        help.queryNoticeMsgList();
-
         // 倒计时业务 恢复中间UI
         defaultCd = new CountDownTimer(1000, 1000) {
             @Override
@@ -206,7 +233,11 @@ public class HomeActivity extends BaseCountDownActivity<ActivityHomeBinding> imp
 
         // 身份验证
         binding.accessVerify.setOnClickListener(new FastClickDelegate(view -> {
-            dispatchAccessVerify(AccessVerify.NORMAL);
+            if (SyncFaceTool.INSTANCE.isSyncFace) {
+                showToast("正在同步人脸,请稍后重试!");
+            } else {
+                dispatchAccessVerify(AccessVerify.NORMAL);
+            }
             return null;
         }));
         binding.singIn.setOnClickListener(new FastClickDelegate(view -> {
@@ -267,6 +298,10 @@ public class HomeActivity extends BaseCountDownActivity<ActivityHomeBinding> imp
     @Override
     protected void onResume() {
         super.onResume();
+        // 接口业务
+        help.queryBulletinBoard();
+        help.queryPerson();
+        help.queryNoticeMsgList();
     }
 
     @Override

+ 15 - 37
app/src/main/java/xn/xxp/HomeActivityHelp.java

@@ -16,7 +16,6 @@ import http.vo.response.LabWarnVo;
 import io.reactivex.rxjava3.functions.Consumer;
 import xn.xxp.home.adapter.HomeBoardAdapter;
 import xn.xxp.app.LabApp;
-import xn.xxp.home.adapter.PersonFlipperAdapter;
 import xn.xxp.room.RoomTool;
 import xn.xxp.room.bean.NoticeSummary;
 import xn.xxp.room.dao.NoticeSummaryDao;
@@ -139,59 +138,38 @@ public class HomeActivityHelp {
     }
 
 
-    private PersonFlipperAdapter personFlipperAdapter, experimentAdapter, accessAdapter;
-
     private void dispatchLabPerson(HomeRightResp data) {
-        LogUtils.json(data);
         // 值班人员
         if (null != data.dutyUser && !data.dutyUser.isEmpty()) {
-            if (null == personFlipperAdapter) {
-                personFlipperAdapter = new PersonFlipperAdapter(PersonFlipperAdapter.TYPE_DUTY, data.dutyUser);
-                activity.binding.dutyPerson.setAdapter(personFlipperAdapter);
-                // 翻转间隔
-                activity.binding.dutyPerson.setFlipInterval(3000);
-                activity.binding.dutyPerson.setEmptyView(activity.binding.dutyPersonEmpty);
-                activity.binding.dutyPerson.setInAnimation(activity, R.animator.anim_in);
-                activity.binding.dutyPerson.setOutAnimation(activity, R.animator.anim_out);
-            }
-            personFlipperAdapter.setData(data.dutyUser);
-            personFlipperAdapter.notifyDataSetChanged();
+            activity.personList.clear();
+            activity.personList.addAll(data.dutyUser);
+            activity.personFlipperAdapter.notifyDataSetChanged();
             activity.binding.dutyPerson.startFlipping();
         } else {
+            activity.personList.clear();
+            activity.personFlipperAdapter.notifyDataSetChanged();
             activity.binding.dutyPerson.stopFlipping();
         }
         // 实验人员
         if (null != data.tentativeUser && !data.tentativeUser.isEmpty()) {
-            if (null == experimentAdapter) {
-                experimentAdapter = new PersonFlipperAdapter(PersonFlipperAdapter.TYPE_EXPERIMENT, data.tentativeUser);
-                activity.binding.experimentPerson.setAdapter(experimentAdapter);
-                // 翻转间隔
-                activity.binding.experimentPerson.setFlipInterval(3000);
-                activity.binding.experimentPerson.setEmptyView(activity.binding.experimentPersonEmpty);
-                activity.binding.experimentPerson.setInAnimation(activity, R.animator.anim_in);
-                activity.binding.experimentPerson.setOutAnimation(activity, R.animator.anim_out);
-            }
-            experimentAdapter.setData(data.tentativeUser);
-            experimentAdapter.notifyDataSetChanged();
+            activity.experimentList.clear();
+            activity.experimentList.addAll(data.tentativeUser);
+            activity.experimentAdapter.notifyDataSetChanged();
             activity.binding.experimentPerson.startFlipping();
         } else {
+            activity.experimentList.clear();
+            activity.experimentAdapter.notifyDataSetChanged();
             activity.binding.experimentPerson.stopFlipping();
         }
         // 准入人员
         if (null != data.securityUser && !data.securityUser.isEmpty()) {
-            if (null == accessAdapter) {
-                accessAdapter = new PersonFlipperAdapter(PersonFlipperAdapter.TYPE_ACCESS, data.securityUser);
-                activity.binding.accessPerson.setAdapter(accessAdapter);
-                // 翻转间隔
-                activity.binding.accessPerson.setFlipInterval(3000);
-                activity.binding.accessPerson.setEmptyView(activity.binding.experimentPersonEmpty);
-                activity.binding.accessPerson.setInAnimation(activity, R.animator.anim_in);
-                activity.binding.accessPerson.setOutAnimation(activity, R.animator.anim_out);
-            }
-            accessAdapter.setData(data.securityUser);
-            accessAdapter.notifyDataSetChanged();
+            activity.accessList.clear();
+            activity.accessList.addAll(data.securityUser);
+            activity.accessAdapter.notifyDataSetChanged();
             activity.binding.accessPerson.startFlipping();
         } else {
+            activity.accessList.clear();
+            activity.accessAdapter.notifyDataSetChanged();
             activity.binding.accessPerson.stopFlipping();
         }
     }

+ 1 - 0
app/src/main/java/xn/xxp/app/InitActivity.java

@@ -253,6 +253,7 @@ public class InitActivity extends BaseActivity<ActivityInitBinding> {
                 int code = jsonObject.getInt("code");
                 if (200 == code) {
                     LabConfig labConfig = GsonUtils.fromJson(jsonObject.getJSONObject("data").toString(), LabConfig.class);
+                    RoomTool.getInstance().labConfigDao().clear();
                     RoomTool.getInstance().labConfigDao().insert(labConfig);
                     reTryCount = 0;
                     return;

+ 1 - 0
app/src/main/java/xn/xxp/app/LabApp.java

@@ -54,6 +54,7 @@ public class LabApp extends Application {
     @Override
     public void onCreate() {
         super.onCreate();
+        Tool.INSTANCE.openAdb();
         gson = GsonUtils.getGson().newBuilder().serializeNulls().registerTypeAdapter(String.class, new StringNullAdapter()).create();
         GsonUtils.setGsonDelegate(gson);
         GsonUtils.setGson("defaultGson", gson);

+ 37 - 14
app/src/main/java/xn/xxp/app/SyncFaceTool.java

@@ -1,6 +1,7 @@
 package xn.xxp.app;
 
 import android.annotation.SuppressLint;
+import android.app.Activity;
 import android.app.DownloadManager;
 import android.content.Context;
 import android.net.Uri;
@@ -10,11 +11,14 @@ import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
 import androidx.core.util.Pair;
 
+import com.blankj.utilcode.util.ActivityUtils;
 import com.blankj.utilcode.util.FileUtils;
 import com.blankj.utilcode.util.GsonUtils;
 import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ThreadUtils;
 import com.blankj.utilcode.util.Utils;
 import com.google.gson.reflect.TypeToken;
 import com.njlz.event.BuildModelEvent;
@@ -36,6 +40,7 @@ import java.util.Map;
 
 import http.client.HttpTool;
 import okhttp3.Response;
+import xn.xxp.home.auth.ChoiceAuthActivity;
 import xn.xxp.room.RoomTool;
 import xn.xxp.room.bean.Face;
 import xn.xxp.room.dao.FaceDao;
@@ -48,13 +53,23 @@ public enum SyncFaceTool {
     private long lastBuildTime = 0;
     private volatile boolean isBuildFaceEnd = false;
     private List<Face> faceList;
-    private volatile boolean isSyncFace = false;
+    public volatile boolean isSyncFace = false;
 
     SyncFaceTool() {
 
     }
 
+    @WorkerThread
     public void doWork() {
+        if (ThreadUtils.isMainThread()) {
+            LogUtils.e("该方法必须在子线程调用");
+            return;
+        }
+        Activity topActivity = ActivityUtils.getTopActivity();
+        if (topActivity instanceof ChoiceAuthActivity) {
+            LogUtils.d("人脸识别中,不去同步人脸!");
+            return;
+        }
         if (isSyncFace) {
             LogUtils.d("正在同步人脸");
             return;
@@ -64,15 +79,16 @@ public enum SyncFaceTool {
         }
         // 初始化
         isSyncFace = true;
-        FaceUtils.getInstance().init(Utils.getApp());
-        EventBus.getDefault().register(this);
-        // 同步人脸
         try {
+            // 同步人脸
+            FaceUtils.getInstance().init(Utils.getApp());
+            EventBus.getDefault().register(this);
             // http请求同步
             List<Pair<String, String>> faceUrlList = getLabFaceList();
             if (null != faceUrlList && !faceUrlList.isEmpty()) {
                 // 下载人脸
                 List<FaceBuildBean> faceBuildBeanList = downloadPicAndBuild(faceUrlList);
+                LogUtils.json("需要变更的人脸", faceBuildBeanList);
                 // 人脸建模
                 if (null != faceBuildBeanList && !faceBuildBeanList.isEmpty()) {
                     FaceUtils.getInstance().faceBuild(faceBuildBeanList);
@@ -85,15 +101,24 @@ public enum SyncFaceTool {
                         }
                     }
                 }
-                // 删除本地多余的人脸
-                delRedundantFace();
+                LogUtils.json(RoomTool.getInstance().faceDao().getAll());
+            } else {
+                LogUtils.d("不需要下载变更人脸");
             }
+            // 删除本地多余的人脸
+            delRedundantFace();
+            LogUtils.d("底库", FaceUtils.getInstance().getAllFaceId());
         } catch (Exception e) {
             LogUtils.e(Log.getStackTraceString(e));
+        } finally {
+            try {
+                // 销毁
+                EventBus.getDefault().unregister(this);
+                FaceUtils.getInstance().destroy();
+            } catch (Exception e) {
+                LogUtils.e(Log.getStackTraceString(e));
+            }
         }
-        // 销毁
-        EventBus.getDefault().unregister(this);
-        FaceUtils.getInstance().destroy();
         isSyncFace = false;
     }
 
@@ -134,6 +159,8 @@ public enum SyncFaceTool {
                             faceUrlList.add(Pair.create(String.valueOf(face.getUserId()), HttpTool.checkUrl(face.getFaceUrl())));
                         }
                         return faceUrlList;
+                    } else {
+                        return new ArrayList<>();
                     }
                 }
             }
@@ -237,11 +264,7 @@ public enum SyncFaceTool {
                         if (null == count) {
                             fileStateMap.put(path, 1);
                         } else if (count >= 2) {
-                            for (File file : downloadList) {
-                                if (path.equals(file.getName())) {
-                                    downloadList.remove(file);
-                                }
-                            }
+                            downloadList.removeIf(file -> path.equals(file.getName()));
                         } else {
                             fileStateMap.put(path, count + 1);
                         }

+ 2 - 3
app/src/main/java/xn/xxp/home/AccessVerify.kt

@@ -6,9 +6,8 @@ package xn.xxp.home
  * @author ReiChin_
  */
 enum class AccessVerify(val type: String, val desc: String) {
-
     NORMAL("1", "普通核验"),
     SIGN_IN("2", "签到"),
-    SIGN_OUT("3", "签退")
-
+    SIGN_OUT("3", "签退"),
+    JUST_OPEN_DOOR("4", "开门"),
 }

+ 106 - 0
app/src/main/java/xn/xxp/home/adapter/PeopleFlipperAdapter.java

@@ -0,0 +1,106 @@
+package xn.xxp.home.adapter;
+
+import android.annotation.SuppressLint;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import com.blankj.utilcode.util.LogUtils;
+import com.bumptech.glide.Glide;
+
+import java.util.List;
+
+import http.vo.response.LabPersonVo;
+import xn.xxp.R;
+import xn.xxp.databinding.ItemHomePersonFlipperBinding;
+
+public class PeopleFlipperAdapter extends BaseAdapter {
+    private final int type;
+    private final List<LabPersonVo> labPersonVoList;
+
+    public PeopleFlipperAdapter(int type, List<LabPersonVo> labPersonVoList) {
+        this.type = type;
+        this.labPersonVoList = labPersonVoList;
+    }
+
+    @Override
+    public int getCount() {
+        return null == labPersonVoList ? 0 : labPersonVoList.size();
+    }
+
+    @Override
+    public LabPersonVo getItem(int position) {
+        return null == labPersonVoList || labPersonVoList.isEmpty() ? null : labPersonVoList.get(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return 0;
+    }
+
+    @SuppressLint("SetTextI18n")
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ViewHolder viewHolder;
+        if (convertView == null) {
+            ItemHomePersonFlipperBinding binding = ItemHomePersonFlipperBinding.inflate(LayoutInflater.from(parent.getContext()));
+            convertView = binding.getRoot();
+            viewHolder = new ViewHolder(binding);
+            convertView.setTag(viewHolder);
+        } else {
+            viewHolder = (ViewHolder) convertView.getTag();
+        }
+        LabPersonVo labPersonVo = getItem(position);
+        if (null != labPersonVo) {
+            ItemHomePersonFlipperBinding binding = viewHolder.binding;
+            if (!TextUtils.isEmpty(labPersonVo.avatar)) {
+                Glide.with(binding.pPhoto)
+                        .load(labPersonVo.avatar)
+                        .placeholder(R.mipmap.icon_sign_in_avatar)
+                        .error(R.mipmap.icon_sign_in_avatar)
+                        .into(binding.pPhoto);
+            }
+            binding.pName.setText(TextUtils.isEmpty(labPersonVo.userName) ? "" : labPersonVo.userName);
+            // 值班人员
+            if (type == 1) {
+                binding.pCardNumber.setText(TextUtils.isEmpty(labPersonVo.userPhone) ? "" : labPersonVo.userPhone);
+            }
+            // 实验室人员
+            else if (type == 2) {
+                try {
+                    String signTime = labPersonVo.signTime;
+                    if (!TextUtils.isEmpty(signTime)) {
+                        signTime = signTime.substring(5, 16);
+                        binding.pCardNumber.setText("签到时间:" + signTime);
+                    }
+                } catch (Exception e) {
+                    LogUtils.e(Log.getStackTraceString(e));
+                }
+            }
+            // 准入人员
+            else if (type == 3) {
+                try {
+                    String validEndTime = labPersonVo.validEndTime;
+                    if (!TextUtils.isEmpty(validEndTime)) {
+                        validEndTime = validEndTime.substring(2);
+                        binding.pCardNumber.setText("准入期限:" + validEndTime);
+                    }
+                } catch (Exception e) {
+                    LogUtils.e(Log.getStackTraceString(e));
+                }
+            }
+        }
+        return convertView;
+    }
+
+    static class ViewHolder {
+        private final ItemHomePersonFlipperBinding binding;
+
+        public ViewHolder(ItemHomePersonFlipperBinding binding) {
+            this.binding = binding;
+        }
+    }
+}

+ 0 - 90
app/src/main/java/xn/xxp/home/adapter/PersonFlipperAdapter.kt

@@ -1,90 +0,0 @@
-package xn.xxp.home.adapter
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.BaseAdapter
-import android.widget.ImageView
-import android.widget.TextView
-import com.bumptech.glide.Glide
-import http.vo.response.LabPersonVo
-import xn.xxp.R
-import xn.xxp.databinding.ItemHomePersonFlipperBinding
-
-public class PersonFlipperAdapter(private val type: Int, var data: List<LabPersonVo>? = null) :
-    BaseAdapter() {
-    companion object {
-        // 值班人员
-        const val TYPE_DUTY = 1
-
-        // 实验人员
-        const val TYPE_EXPERIMENT = 2
-
-        // 准入人员
-        const val TYPE_ACCESS = 3
-    }
-
-    override fun getCount() = data?.size ?: 0
-
-    override fun getItem(position: Int): LabPersonVo? {
-        return data?.get(position)
-    }
-
-    override fun getItemId(position: Int) = position.toLong()
-
-    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
-        val rootView: View
-        val viewHolder: ViewHolder
-        if (convertView == null) {
-            val viewBinding =
-                ItemHomePersonFlipperBinding.inflate(LayoutInflater.from(parent!!.context))
-            rootView = viewBinding.root
-            viewHolder = ViewHolder(viewBinding.pPhoto, viewBinding.pName, viewBinding.pCardNumber)
-            rootView.tag = viewHolder
-        } else {
-            rootView = convertView
-            viewHolder = convertView.tag as ViewHolder
-        }
-
-        getItem(position)?.let {
-            Glide.with(rootView.context)
-                .load(it.avatar)
-                .placeholder(R.mipmap.icon_sign_in_avatar)
-                .error(R.mipmap.icon_sign_in_avatar)
-//                .transform(RoundedCorners(DeviceUtils.dip2px(context, 6f)))
-                .into(viewHolder.imageView)
-
-            viewHolder.name.text = it.userName
-
-            when (type) {
-                // 值班人员
-                TYPE_DUTY -> viewHolder.number.text = it.userPhone
-
-                // 实验人员
-                TYPE_EXPERIMENT -> viewHolder.number.text =
-                    "签到时间:${formatSignTime(it.signTime)}"
-
-                // 准入人员
-                TYPE_ACCESS -> viewHolder.number.text =
-                    "准入期限:${formatValidTime(it.validEndTime)}"
-            }
-        }
-        return rootView
-    }
-
-    private fun formatValidTime(input: String?): String {
-        // 2023-10-10 => 23-10-10
-        return if (input.isNullOrEmpty()) "" else input.substring(2, input.length)
-    }
-
-    private fun formatSignTime(time: String?): CharSequence {
-        // 2023-03-30 15:45:25 => 03-30 15:45
-        return if (time.isNullOrEmpty()) "" else time.subSequence(5, 16)
-    }
-
-    class ViewHolder(
-        val imageView: ImageView,
-        val name: TextView,
-        val number: TextView
-    )
-}

+ 7 - 5
app/src/main/java/xn/xxp/home/auth/ChoiceAuthActivity.java

@@ -10,8 +10,10 @@ import com.blankj.utilcode.util.FragmentUtils;
 
 import core.ui.activity.BaseSignActivity;
 import http.vo.response.UserVo;
+import xn.xxp.HomeActivity;
 import xn.xxp.R;
 import xn.xxp.app.LabApp;
+import xn.xxp.app.SyncFaceTool;
 import xn.xxp.databinding.ActivityAuthChoiceBinding;
 import xn.xxp.home.AccessVerify;
 import xn.xxp.home.auth.fragment.CardAuthFragment;
@@ -95,16 +97,15 @@ public class ChoiceAuthActivity extends BaseSignActivity<ActivityAuthChoiceBindi
     public void authSuccess(AuthType authType, UserVo user) {
         LabApp.userVo = user;
         if (accessVerify.equals(AccessVerify.NORMAL.getType())) {
-            if ("1".equals(labConfig.getIsRelationGuard())) {
-                Tool.INSTANCE.openDoor();
-            } else {
-                ActivityUtils.startActivity(MainActivity.class);
-            }
+            ActivityUtils.startActivity(MainActivity.class);
             finish();
         } else if (accessVerify.equals(AccessVerify.SIGN_IN.getType())) {
             dispatchSignIn();
         } else if (accessVerify.equals(AccessVerify.SIGN_OUT.getType())) {
             dispatchSignOut();
+        } else if (accessVerify.equals(AccessVerify.JUST_OPEN_DOOR.getType())) {
+            Tool.INSTANCE.openDoor();
+            ActivityUtils.finishToActivity(HomeActivity.class, false);
         }
     }
 
@@ -112,6 +113,7 @@ public class ChoiceAuthActivity extends BaseSignActivity<ActivityAuthChoiceBindi
     protected void onSignInFinish(boolean completed) {
         super.onSignInFinish(completed);
         if (completed) LabApp.userVo = null;
+        Tool.INSTANCE.openDoor();
         finish();
     }
 

+ 2 - 1
app/src/main/java/xn/xxp/home/auth/OnAuthResultListener.kt

@@ -21,5 +21,6 @@ enum class AuthType(val type: Int, val desc: String) {
     CARD(1, "card"),
     FACE(2, "face"),
     FINGER(3, "finger"),
-    PASSWORD(4, "password")
+    PASSWORD(4, "password"),
+    QR(5, "qr")
 }

+ 86 - 60
app/src/main/java/xn/xxp/home/auth/fragment/LzFaceAuthFragment.java

@@ -15,6 +15,7 @@ import android.view.ViewTreeObserver;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.blankj.utilcode.util.ActivityUtils;
 import com.blankj.utilcode.util.GsonUtils;
 import com.blankj.utilcode.util.LogUtils;
 import com.blankj.utilcode.util.ThreadUtils;
@@ -38,6 +39,7 @@ import http.client.HttpTool;
 import http.vo.response.UserVo;
 import kotlin.Triple;
 import okhttp3.Response;
+import xn.xxp.HomeActivity;
 import xn.xxp.databinding.FragmentAuthLzFaceBinding;
 import xn.xxp.home.auth.AuthType;
 import xn.xxp.home.auth.ChoiceAuthActivity;
@@ -64,14 +66,20 @@ public class LzFaceAuthFragment extends RcBaseFragment<FragmentAuthLzFaceBinding
 
     @Override
     protected void initViews(@Nullable Bundle savedInstanceState) {
-        FaceUtils.getInstance().init(getActivity());
-        EventBus.getDefault().register(this);
-        binding.faceFRV.setDrawConfig(Color.GREEN, 1, false, false);
-        surfaceHolder = binding.faceSV.getHolder();
-        surfaceHolder.addCallback(this);
-        binding.faceSV.getViewTreeObserver().addOnGlobalLayoutListener(this);
-        faceList = RoomTool.getInstance().faceDao().getAll();
-        labConfig = RoomTool.getInstance().labConfigDao().getLabConfig();
+        try {
+            FaceUtils.getInstance().init(getActivity());
+            EventBus.getDefault().register(this);
+            binding.faceFRV.setDrawConfig(Color.GREEN, 1, false, false);
+            surfaceHolder = binding.faceSV.getHolder();
+            surfaceHolder.addCallback(this);
+            binding.faceSV.getViewTreeObserver().addOnGlobalLayoutListener(this);
+            faceList = RoomTool.getInstance().faceDao().getAll();
+            labConfig = RoomTool.getInstance().labConfigDao().getLabConfig();
+        } catch (Exception e) {
+            LogUtils.e(Log.getStackTraceString(e));
+            showToast("正在同步人脸,请稍后重试!");
+            ActivityUtils.finishToActivity(HomeActivity.class, false);
+        }
     }
 
     @Subscribe(threadMode = ThreadMode.ASYNC)
@@ -101,6 +109,8 @@ public class LzFaceAuthFragment extends RcBaseFragment<FragmentAuthLzFaceBinding
 
     @Override
     public void onDestroyView() {
+        ThreadUtils.cancel(faceTask);
+        ThreadUtils.cancel(labUserTask);
         super.onDestroyView();
         EventBus.getDefault().unregister(this);
         FaceUtils.getInstance().destroy();
@@ -159,16 +169,20 @@ public class LzFaceAuthFragment extends RcBaseFragment<FragmentAuthLzFaceBinding
     }
 
 
+    ThreadUtils.SimpleTask<UserVo> faceTask;
+    ThreadUtils.SimpleTask<Triple<Integer, String, UserVo>> labUserTask;
+
     /**
      * 获取识别结果
      */
     public void showRecognitionResult() {
-        if (!checking) {
+        if (!checking && !isDetached()) {
             checking = true;
             FaceUtils.getInstance().stopFaceTask();
             FaceIdentifyData[] datas = FaceUtils.getInstance().getIdentifyResultDatas();
             if (datas != null && datas.length > 0) {
-                ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<UserVo>() {
+                ThreadUtils.cancel(faceTask);
+                faceTask = new ThreadUtils.SimpleTask<UserVo>() {
                     @Override
                     public UserVo doInBackground() throws Throwable {
                         FaceIdentifyData faceIdentifyData = datas[0];
@@ -194,65 +208,77 @@ public class LzFaceAuthFragment extends RcBaseFragment<FragmentAuthLzFaceBinding
 
                     @Override
                     public void onSuccess(UserVo result) {
-                        if (result == null) {
-                            ThreadUtils.runOnUiThreadDelayed(() -> {
-                                checking = false;
-                                FaceUtils.getInstance().startFaceRecog();
-                            }, 1000);
-                            showToast("人员未授权,请重试!");
-                        } else {
-                            ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Triple<Integer, String, UserVo>>() {
-                                @Override
-                                public Triple<Integer, String, UserVo> doInBackground() throws Throwable {
-                                    try {
-                                        Response response = HttpTool.getLabStatusById(String.valueOf(labConfig.getLabId()), result.userId);
-                                        if (response.isSuccessful()) {
-                                            String json = response.body().string();
-                                            JSONObject jsonObject = new JSONObject(json);
-                                            int code = jsonObject.getInt("code");
-                                            String message = jsonObject.getString("message");
-                                            if (200 == code) {
-                                                UserVo userVo = GsonUtils.fromJson(jsonObject.getJSONObject("data").toString(), UserVo.class);
-                                                return new Triple<>(code, TextUtils.isEmpty(message) ? "" : message, userVo);
+                        if (!isDetached()) {
+                            if (result == null) {
+                                ThreadUtils.runOnUiThreadDelayed(() -> {
+                                    checking = false;
+                                    FaceUtils.getInstance().startFaceRecog();
+                                }, 1000);
+                                showToast("人员未授权,请重试!");
+                            } else {
+                                ThreadUtils.cancel(labUserTask);
+                                labUserTask = new ThreadUtils.SimpleTask<Triple<Integer, String, UserVo>>() {
+                                    @Override
+                                    public Triple<Integer, String, UserVo> doInBackground() throws Throwable {
+                                        try {
+                                            Response response = HttpTool.getLabStatusById(String.valueOf(labConfig.getLabId()), result.userId);
+                                            if (response.isSuccessful()) {
+                                                String json = response.body().string();
+                                                JSONObject jsonObject = new JSONObject(json);
+                                                int code = jsonObject.getInt("code");
+                                                String message = jsonObject.getString("message");
+                                                if (200 == code) {
+                                                    UserVo userVo = GsonUtils.fromJson(jsonObject.getJSONObject("data").toString(), UserVo.class);
+                                                    return new Triple<>(code, TextUtils.isEmpty(message) ? "" : message, userVo);
+                                                }
+                                                return new Triple<>(code, TextUtils.isEmpty(message) ? "" : message, result);
                                             }
-                                            return new Triple<>(code, TextUtils.isEmpty(message) ? "" : message, result);
+                                        } catch (Exception e) {
+                                            LogUtils.e(Log.getStackTraceString(e));
                                         }
-                                    } catch (Exception e) {
-                                        LogUtils.e(Log.getStackTraceString(e));
+                                        return null;
                                     }
-                                    return null;
-                                }
 
-                                @Override
-                                public void onSuccess(Triple<Integer, String, UserVo> result) {
-                                    if (null == result) {
-                                        showToast("查询异常,请重试!");
-                                        ThreadUtils.runOnUiThreadDelayed(() -> {
-                                            checking = false;
-                                            FaceUtils.getInstance().startFaceRecog();
-                                        }, 1000);
-                                        return;
-                                    }
-                                    if (result.getFirst() != 200) {
-                                        showToast("查询异常,请重试!");
-                                        ThreadUtils.runOnUiThreadDelayed(() -> {
-                                            checking = false;
-                                            FaceUtils.getInstance().startFaceRecog();
-                                        }, 1000);
-                                        showToast(result.getSecond());
-                                    } else {
-                                        ChoiceAuthActivity fourChoiceAuthActivity = (ChoiceAuthActivity) LzFaceAuthFragment.this.requireActivity();
-                                        fourChoiceAuthActivity.authSuccess(AuthType.FACE, result.getThird());
+                                    @Override
+                                    public void onSuccess(Triple<Integer, String, UserVo> result) {
+                                        if (!isDetached()) {
+                                            if (null == result) {
+                                                showToast("查询异常,请重试!");
+                                                ThreadUtils.runOnUiThreadDelayed(() -> {
+                                                    if (!isDetached()) {
+                                                        checking = false;
+                                                        FaceUtils.getInstance().startFaceRecog();
+                                                    }
+                                                }, 1000);
+                                                return;
+                                            }
+                                            if (result.getFirst() != 200) {
+                                                ThreadUtils.runOnUiThreadDelayed(() -> {
+                                                    if (!isDetached()) {
+                                                        checking = false;
+                                                        FaceUtils.getInstance().startFaceRecog();
+                                                    }
+                                                }, 1000);
+                                                showToast(result.getSecond());
+                                            } else {
+                                                ChoiceAuthActivity fourChoiceAuthActivity = (ChoiceAuthActivity) LzFaceAuthFragment.this.requireActivity();
+                                                fourChoiceAuthActivity.authSuccess(AuthType.FACE, result.getThird());
+                                            }
+                                        }
                                     }
-                                }
-                            });
+                                };
+                                ThreadUtils.executeByCached(labUserTask);
+                            }
                         }
                     }
-                });
+                };
+                ThreadUtils.executeByCached(faceTask);
             } else {
                 ThreadUtils.runOnUiThreadDelayed(() -> {
-                    checking = false;
-                    FaceUtils.getInstance().startFaceRecog();
+                    if (!isDetached()) {
+                        checking = false;
+                        FaceUtils.getInstance().startFaceRecog();
+                    }
                 }, 1000);
             }
         }

+ 64 - 23
app/src/main/java/xn/xxp/home/auth/fragment/QrAuthFragment.java

@@ -11,6 +11,7 @@ import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.GsonUtils;
 import com.blankj.utilcode.util.LogUtils;
 import com.blankj.utilcode.util.ThreadUtils;
 import com.blankj.utilcode.util.TimeUtils;
@@ -19,13 +20,18 @@ import com.google.zxing.MultiFormatWriter;
 import com.google.zxing.WriterException;
 import com.google.zxing.common.BitMatrix;
 
+import org.json.JSONObject;
+
 import java.text.SimpleDateFormat;
 import java.util.Locale;
 
 import core.ui.fragment.RcBaseFragment;
 import http.client.HttpTool;
+import http.vo.response.UserVo;
 import okhttp3.Response;
 import xn.xxp.databinding.FragmentAuthQrBinding;
+import xn.xxp.home.auth.AuthType;
+import xn.xxp.home.auth.ChoiceAuthActivity;
 import xn.xxp.room.RoomTool;
 import xn.xxp.room.bean.DeviceConfig;
 import xn.xxp.room.bean.LabConfig;
@@ -57,34 +63,42 @@ public class QrAuthFragment extends RcBaseFragment<FragmentAuthQrBinding> {
         ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Bitmap>() {
             @Override
             public Bitmap doInBackground() throws Throwable {
-                qrScanTime = TimeUtils.getNowString(new SimpleDateFormat("HHmmssSSS", Locale.getDefault()));
-                String qrUrl;
-                if (AppUtils.isAppDebug()) {
-                    qrUrl = deviceConfig.getBaseUrl() + "?macId=" + deviceConfig.getDevId() + "&subId=" + labConfig.getLabId() + "&type=13&code=" + qrScanTime;
-                } else {
-                    qrUrl = "https://labcontrol.nwafu.edu.cn/api/?macId=" + deviceConfig.getDevId() + "&subId=" + labConfig.getLabId() + "&type=13&code=" + qrScanTime;
-                }
-                LogUtils.d(qrUrl);
-                return createQrCode(qrUrl);
+                createQrCodeInfo();
+                return null;
             }
 
             @Override
             public void onSuccess(Bitmap bitmap) {
-                if (null != bitmap) {
-                    binding.qrIV.setBackground(null);
-                    binding.qrIV.setImageBitmap(bitmap);
-                    startQrTimer();
-                }
-
+                startQrTimer();
             }
         });
 
     }
 
+    private void createQrCodeInfo() {
+        qrScanTime = TimeUtils.getNowString(new SimpleDateFormat("HHmmssSSS", Locale.getDefault()));
+        String qrUrl;
+        if (AppUtils.isAppDebug()) {
+            qrUrl = deviceConfig.getBaseUrl() + "?macId=" + deviceConfig.getDevId() + "&subId=" + labConfig.getLabId() + "&type=13&code=" + qrScanTime;
+        } else {
+            qrUrl = "https://labcontrol.nwafu.edu.cn/api/?macId=" + deviceConfig.getDevId() + "&subId=" + labConfig.getLabId() + "&type=13&code=" + qrScanTime;
+        }
+        LogUtils.d("二维码", qrUrl);
+        Bitmap bitmap = createQrCode(qrUrl);
+        if (null != bitmap) {
+            ThreadUtils.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    binding.qrIV.setBackground(null);
+                    binding.qrIV.setImageBitmap(bitmap);
+                }
+            });
+        }
+    }
+
     private CountDownTimer qrTimer;
 
     private void startQrTimer() {
-        // TODO 需要优化生命周期
         if (null != qrTimer) {
             qrTimer.cancel();
             qrTimer = null;
@@ -97,21 +111,46 @@ public class QrAuthFragment extends RcBaseFragment<FragmentAuthQrBinding> {
 
             @Override
             public void onFinish() {
-                ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
+                ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<UserVo>() {
                     @Override
-                    public Object doInBackground() throws Throwable {
-                        Response response = HttpTool.infobordLogin(deviceConfig.getDevId(), qrScanTime);
+                    public UserVo doInBackground() throws Throwable {
+                        Response response = HttpTool.infoBordLogin(deviceConfig.getDevId(), qrScanTime);
                         String json = response.body().string();
-                        LogUtils.d(json);
+                        JSONObject jsonObject = new JSONObject(json);
+                        int code = jsonObject.getInt("code");
+                        if (200 == code) {
+                            LogUtils.d("扫码验证通过", json);
+                            qrTimer.cancel();
+                            UserVo userVo = GsonUtils.fromJson(jsonObject.getJSONObject("data").toString(), UserVo.class);
+                            Response labStatusResponse = HttpTool.getLabStatusById(String.valueOf(labConfig.getLabId()), userVo.userId);
+                            String labStatusJson = labStatusResponse.body().string();
+                            JSONObject labStatusJO = new JSONObject(labStatusJson);
+                            int labStatusCode = labStatusJO.getInt("code");
+                            if (200 == labStatusCode) {
+                                return GsonUtils.fromJson(jsonObject.getJSONObject("data").toString(), UserVo.class);
+                            } else {
+                                createQrCodeInfo();
+                                qrTimer.start();
+                            }
+                            return null;
+                        } else {
+                            LogUtils.d("等待扫码");
+                            qrTimer.start();
+                        }
                         return null;
                     }
 
                     @Override
-                    public void onSuccess(Object result) {
-
+                    public void onSuccess(UserVo result) {
+                        if (!isDetached()) {
+                            if (null != result) {
+                                ChoiceAuthActivity fourChoiceAuthActivity = (ChoiceAuthActivity) QrAuthFragment.this.requireActivity();
+                                fourChoiceAuthActivity.authSuccess(AuthType.QR, result);
+                            }
+                        }
                     }
                 });
-                qrTimer.start();
+
             }
         };
         qrTimer.start();
@@ -119,6 +158,8 @@ public class QrAuthFragment extends RcBaseFragment<FragmentAuthQrBinding> {
 
     @Override
     public void onDestroyView() {
+        qrTimer.cancel();
+        qrTimer = null;
         super.onDestroyView();
 
     }

+ 1 - 0
app/src/main/java/xn/xxp/home/fragment/LabBasicFragment.kt

@@ -12,6 +12,7 @@ import android.widget.LinearLayout
 import android.widget.TextView
 import androidx.core.content.ContextCompat
 import androidx.recyclerview.widget.GridLayoutManager
+import com.blankj.utilcode.util.LogUtils
 import com.bumptech.glide.Glide
 import com.chad.library.adapter.base.BaseDelegateMultiAdapter
 import com.chad.library.adapter.base.delegate.BaseMultiTypeDelegate

+ 2 - 1
app/src/main/java/xn/xxp/home/sign/SafetyCheckFragment.kt

@@ -379,7 +379,8 @@ class SafetyCheckFragment :
                         3000
                     ).apply {
                         setCancelable(false)
-                        setOnDismissListener { requireActivity().finish() }
+                        setOnDismissListener {
+                            requireActivity().finish() }
                         show()
                     }
                 }

+ 1 - 0
app/src/main/java/xn/xxp/home/window/WindowHorizontalAdapter.java

@@ -75,6 +75,7 @@ public class WindowHorizontalAdapter extends RecyclerView.Adapter<WindowHorizont
     @SuppressLint("NotifyDataSetChanged")
     public void updateData(List<String> classifyList) {
         this.classifyList = classifyList;
+        LogUtils.d(classifyList);
         notifyDataSetChanged();
     }
 

+ 1 - 1
app/src/main/java/xn/xxp/home/window/WindowTextlAdapter.java

@@ -62,7 +62,7 @@ public class WindowTextlAdapter extends RecyclerView.Adapter<WindowTextlAdapter.
                     }
                 }
                 if (stringBuilder.length() > 3) {
-                    stringBuilder.delete(stringBuilder.length() - 3, stringBuilder.length());
+                    stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length());
                 }
                 binding.emergencyContactTV.setText(stringBuilder.toString());
             } else {

+ 8 - 11
app/src/main/java/xn/xxp/home/window/WindowVerticalAdapter.java

@@ -24,9 +24,6 @@ public class WindowVerticalAdapter extends RecyclerView.Adapter<WindowVerticalAd
 
     private Context context;
     private List<SafeClassify> safeClassifyList;
-    private GradientDrawable topAndLeftGD;
-    private GradientDrawable bottomAndLeftGD;
-    private GradientDrawable gradientDrawable;
 
     /**
      * @param safeClassifyList 筛选classifyType为2
@@ -34,14 +31,6 @@ public class WindowVerticalAdapter extends RecyclerView.Adapter<WindowVerticalAd
     public WindowVerticalAdapter(Context context, List<SafeClassify> safeClassifyList) {
         this.context = context;
         this.safeClassifyList = safeClassifyList;
-        topAndLeftGD = new GradientDrawable();
-        topAndLeftGD.setShape(GradientDrawable.RECTANGLE);
-        topAndLeftGD.setCornerRadii(new float[]{10f, 10f, 0f, 0f, 0f, 0f, 0f, 0f,});
-        bottomAndLeftGD = new GradientDrawable();
-        bottomAndLeftGD.setShape(GradientDrawable.RECTANGLE);
-        bottomAndLeftGD.setCornerRadii(new float[]{0f, 0f, 0f, 0f, 0f, 0f, 10f, 10f});
-        gradientDrawable = new GradientDrawable();
-        gradientDrawable.setShape(GradientDrawable.RECTANGLE);
 
 
     }
@@ -62,6 +51,9 @@ public class WindowVerticalAdapter extends RecyclerView.Adapter<WindowVerticalAd
                 if (!TextUtils.isEmpty(safeClassify.showColour)) {
                     try {
                         int color = Color.parseColor(safeClassify.showColour);
+                        GradientDrawable topAndLeftGD = new GradientDrawable();
+                        topAndLeftGD.setShape(GradientDrawable.RECTANGLE);
+                        topAndLeftGD.setCornerRadii(new float[]{10f, 10f, 0f, 0f, 0f, 0f, 0f, 0f,});
                         topAndLeftGD.setColor(color);
                         binding.safeClassifyTV.setBackground(topAndLeftGD);
                     } catch (Exception e) {
@@ -74,6 +66,9 @@ public class WindowVerticalAdapter extends RecyclerView.Adapter<WindowVerticalAd
                 if (!TextUtils.isEmpty(safeClassify.showColour)) {
                     try {
                         int color = Color.parseColor(safeClassify.showColour);
+                        GradientDrawable bottomAndLeftGD = new GradientDrawable();
+                        bottomAndLeftGD.setShape(GradientDrawable.RECTANGLE);
+                        bottomAndLeftGD.setCornerRadii(new float[]{0f, 0f, 0f, 0f, 0f, 0f, 10f, 10f});
                         bottomAndLeftGD.setColor(color);
                         binding.safeClassifyTV.setBackground(bottomAndLeftGD);
                     } catch (Exception e) {
@@ -86,6 +81,8 @@ public class WindowVerticalAdapter extends RecyclerView.Adapter<WindowVerticalAd
                 if (!TextUtils.isEmpty(safeClassify.showColour)) {
                     try {
                         int color = Color.parseColor(safeClassify.showColour);
+                        GradientDrawable gradientDrawable = new GradientDrawable();
+                        gradientDrawable.setShape(GradientDrawable.RECTANGLE);
                         gradientDrawable.setColor(color);
                         binding.safeClassifyTV.setBackground(gradientDrawable);
                     } catch (Exception e) {

+ 11 - 9
app/src/main/java/xn/xxp/main/MainActivity.kt

@@ -48,7 +48,8 @@ import xn.xxp.databinding.ActivityMainBinding
 import xn.xxp.home.lab_info.LabDetailActivity
 import xn.xxp.main.monitor.MonitorListActivity
 import xn.xxp.main.person.LaboratoryPersonActivity
-import xn.xxp.main.risk.RiskActivity
+import xn.xxp.main.risk.RiskListActivity
+import xn.xxp.main.rule.RuleActivity
 import xn.xxp.main.things.ThingsActivity
 import xn.xxp.mqtt.event.BannerEvent
 import xn.xxp.mqtt.event.BulletinBoardEvent
@@ -98,6 +99,10 @@ class MainActivity :
         return null
     }
 
+    override fun enabledBackCountDown(): Boolean {
+        return true
+    }
+
     private val mBulletinBoardAdapter = MainBoardAdapter(R.layout.item_bulletin_board)
 
     private val mCheckMachineMsgAdapter = CheckMachineMsgAdapter(R.layout.item_check_machine_msg)
@@ -351,8 +356,8 @@ class MainActivity :
         // 规章制度
         binding.safetyRegulation.setOnClickListener(FastClickDelegate {
             // TODO 规章制度需要手动用java重写
-//            val intent = Intent(this, SafetyRegulationActivity::class.java)
-//            startActivity(intent)
+            val intent = Intent(this, RuleActivity::class.java)
+            startActivity(intent)
         })
         // MSDS
         binding.learnMaterial.setOnClickListener(FastClickDelegate {
@@ -361,7 +366,7 @@ class MainActivity :
         })
         // 危险源
         binding.sourceOfRisk.setOnClickListener(FastClickDelegate {
-            val intent = Intent(this, RiskActivity::class.java)
+            val intent = Intent(this, RiskListActivity::class.java)
             startActivity(intent)
         })
         // 物联控制
@@ -379,7 +384,7 @@ class MainActivity :
         })
         // 开门
         binding.openDoor.setOnClickListener(FastClickDelegate {
-            handleOpenDoor()
+            Tool.INSTANCE.openDoor()
         })
         // 返回
         binding.back.setOnClickListener(FastClickDelegate {
@@ -427,10 +432,6 @@ class MainActivity :
         }
     }
 
-    private fun handleOpenDoor() {
-        Tool.INSTANCE.openDoor()
-    }
-
     lateinit var labConfig: LabConfig
     override fun initData() {
         labConfig = RoomTool.getInstance().labConfigDao().labConfig
@@ -494,6 +495,7 @@ class MainActivity :
             showDefaultImg()
             return
         }
+        // TODO
         data.forEachIndexed { index, item ->
             Glide.with(this)
                 .load(item.imgUrl)

+ 1 - 1
app/src/main/java/xn/xxp/main/msds/HtmlFullScreenActivity.kt

@@ -49,7 +49,7 @@ class HtmlFullScreenActivity :
 
         if (!htmlContent.isNullOrEmpty()) {
             val html = StringBuilder()
-            html.append("<HTML><HEAD><LINK href=\"simple_table.css\" type=\"text/css\" rel=\"stylesheet\"/></HEAD><body>")
+            html.append("<HTML><HEAD></HEAD><body>")
             html.append(EscapeUnescape.unescape(htmlContent))
             html.append("</body></HTML>")
 

+ 1 - 1
app/src/main/java/xn/xxp/main/msds/InstructionActivity.kt

@@ -119,7 +119,7 @@ open class InstructionActivity :
             // 文件内容
             if (!data.content.isNullOrEmpty()) {
                 val html = StringBuilder()
-                html.append("<HTML><HEAD><LINK href=\"simple_table.css\" type=\"text/css\" rel=\"stylesheet\"/></HEAD><body>")
+                html.append("<HTML><HEAD></HEAD><body>")
                 html.append(EscapeUnescape.unescape(data.content))
                 html.append("</body></HTML>")
 

+ 12 - 0
app/src/main/java/xn/xxp/main/risk/PlayVideoEvent.java

@@ -0,0 +1,12 @@
+package xn.xxp.main.risk;
+
+public class PlayVideoEvent {
+    public String url;
+
+    private PlayVideoEvent() {
+    }
+
+    public PlayVideoEvent(String url) {
+        this.url = url;
+    }
+}

+ 0 - 176
app/src/main/java/xn/xxp/main/risk/RiskActivity.kt

@@ -1,176 +0,0 @@
-package xn.xxp.main.risk
-
-import android.content.Intent
-import android.os.Bundle
-import android.view.View
-import android.widget.TextView
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.blankj.utilcode.util.AppUtils
-import com.bumptech.glide.Glide
-import com.chad.library.adapter.base.BaseQuickAdapter
-import com.chad.library.adapter.base.viewholder.BaseViewHolder
-import core.ui.activity.BaseCountDownActivity
-import core.util.EscapeUnescape
-import core.util.VideoFullScreenWebChromeClient
-import core.util.WebViewHelper
-import http.client.ApiRepository
-import http.vo.request.HazardReq
-import http.vo.response.LabHazardVo
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import xn.xxp.R
-import xn.xxp.databinding.ActivityRiskBinding
-import xn.xxp.main.msds.HtmlFullScreenActivity
-import xn.xxp.room.RoomTool
-import xn.xxp.room.bean.LabConfig
-import xn.xxp.utils.QrTool
-import xn.xxp.widget.ITitleBar
-import xn.xxp.widget.NavViewCompat
-
-/**
- * 危险源
- *
- * @author ReiChin_
- */
-open class RiskActivity :
-    BaseCountDownActivity<ActivityRiskBinding>() {
-
-    override fun createViewBinding() = ActivityRiskBinding.inflate(layoutInflater)
-    override fun getMTitleBar(): ITitleBar {
-        return binding.titleBar
-    }
-
-    override fun getMNavView(): NavViewCompat {
-        return binding.navView
-    }
-
-    private val mRiskAdapter by lazy { RiskAdapter() }
-
-    private var mCurrentData: LabHazardVo? = null
-    lateinit var labConfig: LabConfig
-
-    override fun initViews(savedInstanceState: Bundle?) {
-        super.initViews(savedInstanceState)
-        labConfig = RoomTool.getInstance().labConfigDao().labConfig
-        val webViewHelper = WebViewHelper(binding.webView, binding.progressbar)
-        webViewHelper.initWebView(webChromeClientProxy = VideoFullScreenWebChromeClient(binding.videoFullScreen))
-    }
-
-    override fun initData() {
-        mRiskAdapter.setOnItemClickListener { _, _, position ->
-            val item = mRiskAdapter.getItem(position)
-            mRiskAdapter.selectedItemId = item.hazardId
-            mRiskAdapter.notifyDataSetChanged()
-            refreshDetailUI(item)
-        }
-        binding.risk.layoutManager = LinearLayoutManager(this)
-        binding.risk.adapter = mRiskAdapter
-
-        queryHazardList()
-    }
-
-    private fun queryHazardList() {
-
-        showLoading("加载中...")
-        val param = HazardReq().apply {
-            subId = labConfig.labId.toString()
-            pageNum = 1
-            pageSize = 50
-        }
-        val disposable = ApiRepository.hazardlist(param)
-            .subscribe({ data ->
-                dismissLoading()
-                mRiskAdapter.setNewInstance(data.toMutableList())
-                if (data.isNullOrEmpty()) {
-                    mRiskAdapter.setEmptyView(R.layout.view_list_empty)
-                    binding.qrGroup.visibility = View.INVISIBLE
-                } else {
-                    binding.qrGroup.visibility = View.VISIBLE
-                    // 默认显示第0条数据
-                    mRiskAdapter.selectedItemId = data[0].hazardId
-                    refreshDetailUI(data[0])
-                }
-            }, { throwable ->
-                dismissLoading()
-                showNetError(throwable)
-                throwable.printStackTrace()
-                mRiskAdapter.setEmptyView(R.layout.view_list_empty)
-                binding.qrGroup.visibility = View.INVISIBLE
-            })
-        addDisposable(disposable)
-    }
-
-    private fun refreshDetailUI(data: LabHazardVo?) {
-        this.mCurrentData = data
-        data?.let {
-            // 文件内容
-            if (!data.content.isNullOrEmpty()) {
-                val html = StringBuilder()
-                html.append("<HTML><HEAD><LINK href=\"simple_table.css\" type=\"text/css\" rel=\"stylesheet\"/></HEAD><body>")
-                html.append(EscapeUnescape.unescape(data.content))
-                html.append("</body></HTML>")
-
-                binding.webView.loadDataWithBaseURL(
-                    "file:///android_asset/",
-                    html.toString(),
-                    "text/html",
-                    "utf-8",
-                    null
-                )
-            }
-
-            // 二维码
-            GlobalScope.launch(Dispatchers.Main) {
-                val bitmap = withContext(Dispatchers.IO) {
-                    QrTool.generateQRCode(data.qrCodeUrl, 150)
-                }
-                Glide.with(this@RiskActivity)
-                    .asBitmap()
-                    .load(bitmap)
-                    .error(R.mipmap.img_error)
-                    .into(binding.qrCode)
-            }
-        }
-    }
-
-    override fun initListener() {
-        binding.fullScreen.setOnClickListener {
-            // 全屏
-            mCurrentData?.let { data ->
-                // 进入全屏画面
-                val intent = Intent(this, HtmlFullScreenActivity::class.java).apply {
-                    putExtra("html_content", data.content)
-                    putExtra("onEboard", onEboard())
-                    putExtra("name", data.chName)
-                }
-                startActivity(intent)
-            }
-        }
-    }
-
-    protected open fun onEboard() = true
-
-    override fun enabledBackCountDown() = true
-
-}
-
-/**
- * 危险源列表
- */
-private class RiskAdapter :
-    BaseQuickAdapter<LabHazardVo, BaseViewHolder>(R.layout.item_risk) {
-
-    var selectedItemId: String? = null
-
-    override fun convert(holder: BaseViewHolder, item: LabHazardVo) {
-        val textView = (holder.itemView as TextView)
-        textView.text = "${item.hazardCode ?: ""} ${item.chName ?: ""}"
-        textView.setBackgroundResource(
-            if (selectedItemId == item.hazardId)
-                R.drawable.bg_item_checked else R.drawable.bg_item_normal
-        )
-    }
-
-}

+ 386 - 0
app/src/main/java/xn/xxp/main/risk/RiskListActivity.java

@@ -0,0 +1,386 @@
+package xn.xxp.main.risk;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+
+import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.blankj.utilcode.util.ThreadUtils;
+import com.bumptech.glide.Glide;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import core.ui.activity.BaseCountDownActivity;
+import core.util.EscapeUnescape;
+import http.client.ApiRepository;
+import http.vo.request.HazardReq;
+import http.vo.response.LabHazardVo;
+import io.reactivex.rxjava3.disposables.Disposable;
+import xn.xxp.R;
+import xn.xxp.databinding.ActivityBrowserBinding;
+import xn.xxp.databinding.ActivityRiskBinding;
+import xn.xxp.databinding.ActivityRiskListBinding;
+import xn.xxp.main.msds.HtmlFullScreenActivity;
+import xn.xxp.room.RoomTool;
+import xn.xxp.room.bean.LabConfig;
+import xn.xxp.utils.QrTool;
+import xn.xxp.widget.ITitleBar;
+import xn.xxp.widget.NavViewCompat;
+
+public class RiskListActivity extends BaseCountDownActivity<ActivityRiskListBinding> {
+    private ActivityRiskListBinding binding;
+    private RiskListAdapter riskListAdapter;
+    private LabHazardVo mCurrentData;         // 当前选中的危险源数据
+    private LabConfig labConfig;              // 实验室配置信息
+
+    @Override
+    public ITitleBar getMTitleBar() {
+        return binding.titleBar;
+    }
+
+    @Override
+    public NavViewCompat getMNavView() {
+        return binding.navView;
+    }
+
+    @Override
+    protected ActivityRiskListBinding createViewBinding() {
+        return binding = ActivityRiskListBinding.inflate(getLayoutInflater());
+    }
+
+    @Override
+    protected void initViews(Bundle savedInstanceState) {
+        super.initViews(savedInstanceState);
+        // 初始化实验室配置
+        labConfig = RoomTool.getInstance().labConfigDao().getLabConfig();
+        initWeb();
+    }
+
+    @Override
+    protected void initListener() {
+        // 全屏按钮点击事件
+        binding.fullScreen.setOnClickListener(v -> {
+            if (mCurrentData == null) return;
+            Intent intent = new Intent(this, HtmlFullScreenActivity.class);
+            intent.putExtra("html_content", mCurrentData.content);
+            intent.putExtra("onEboard", true);
+            intent.putExtra("name", mCurrentData.chName);
+            startActivity(intent);
+        });
+    }
+
+    @Override
+    protected void initData() {
+        super.initData();
+        riskListAdapter = new RiskListAdapter(new ArrayList<>());
+        // 设置列表项点击监听
+        riskListAdapter.setOnItemClickListener((adapter, view, position) -> {
+            LabHazardVo item = riskListAdapter.getItem(position);
+            riskListAdapter.setSelectedItemId(item.hazardId);
+            refreshDetailUI(item);
+        });
+
+        // 配置RecyclerView
+        binding.risk.setLayoutManager(new LinearLayoutManager(this));
+        binding.risk.setAdapter(riskListAdapter);
+        // 加载数据
+        queryHazardList();
+    }
+
+    /**
+     * 查询危险源列表数据
+     */
+    private void queryHazardList() {
+        showLoading("加载中...");
+
+        // 构建请求参数
+        HazardReq param = new HazardReq();
+        param.subId = String.valueOf(labConfig.getLabId());
+        param.pageNum = 1;
+        param.pageSize = 20;
+
+        Disposable disposable = ApiRepository.INSTANCE.hazardlist(param)
+                .subscribe(data -> {
+                    dismissLoading();
+                    handleDataSuccess(data);
+                }, throwable -> {
+                    dismissLoading();
+                    handleDataError(throwable);
+                });
+
+        addDisposable(disposable);
+    }
+
+    /**
+     * 处理数据加载成功
+     */
+    private void handleDataSuccess(List<LabHazardVo> data) {
+        riskListAdapter.setNewInstance(data);
+
+        if (data.isEmpty()) {
+            riskListAdapter.setEmptyView(R.layout.view_list_empty);
+            binding.qrGroup.setVisibility(View.INVISIBLE);
+        } else {
+            binding.qrGroup.setVisibility(View.VISIBLE);
+            // 默认选中第一条数据
+            riskListAdapter.setSelectedItemId(data.get(0).hazardId);
+            refreshDetailUI(data.get(0));
+        }
+    }
+
+    /**
+     * 处理数据加载失败
+     */
+    private void handleDataError(Throwable throwable) {
+        showNetError(throwable);
+        riskListAdapter.setEmptyView(R.layout.view_list_empty);
+        binding.qrGroup.setVisibility(View.INVISIBLE);
+    }
+
+    private void refreshDetailUI(LabHazardVo data) {
+        mCurrentData = data;
+        if (data == null) return;
+
+        // 加载HTML内容
+        if (data.content != null && !data.content.isEmpty()) {
+            load(EscapeUnescape.unescape(data.content));
+        }
+
+        // 生成二维码
+        ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Bitmap>() {
+            @Override
+            public Bitmap doInBackground() throws Throwable {
+                return QrTool.generateQRCode(data.qrCodeUrl, 150);
+            }
+
+            @Override
+            public void onSuccess(Bitmap result) {
+                Glide.with(RiskListActivity.this)
+                        .asBitmap()
+                        .load(result)
+                        .error(R.mipmap.img_error)
+                        .into(binding.qrCode);
+            }
+        });
+    }
+
+    private void load(String html) {
+        Document document = Jsoup.parse(html);
+
+        Element head = document.head();
+        // 移除现有的charset meta标签
+        head.select("meta[charset]").remove();
+        head.select("meta[http-equiv=Content-Type]").remove();
+        // 创建并添加新的UTF-8 meta标签
+        Element metaCharset = new Element("meta");
+        metaCharset.attr("charset", "UTF-8");
+        head.prependChild(metaCharset); // 添加到head的开头
+
+        // 删除所有video中的其它属性和内在的内容
+        Elements videos = document.select("video");
+        for (Element video : videos) {
+            // 获取需要保留的属性值
+            String id = video.attr("id");
+            String width = video.attr("width");
+            String height = video.attr("height");
+            String src = video.attr("src");
+            // 清除所有属性
+            video.clearAttributes();
+            // 重新设置需要保留的属性
+            if (!id.isEmpty()) {
+                video.attr("id", id);
+            }
+            if (!width.isEmpty()) {
+                video.attr("width", width);
+            }
+            if (!height.isEmpty()) {
+                video.attr("height", height);
+            }
+            if (!src.isEmpty()) {
+                video.attr("src", src);
+            }
+
+            // 移除video标签内的所有子元素(如source标签)
+            video.html("");
+        }
+
+        // 注入video元素的操作
+        Element script = new Element("script");
+        script.append("// 页面加载完成后执行");
+        script.append("\r\n");
+        script.append("document.addEventListener('DOMContentLoaded', function() {");
+        script.append("\r\n");
+        script.append("// 获取所有video元素");
+        script.append("\r\n");
+        script.append("var videos = document.querySelectorAll('video');");
+        script.append("\r\n");
+        script.append(" var srcDisplay = document.getElementById('srcDisplay');");
+        script.append("\r\n");
+        script.append("// 为每个video元素添加点击事件");
+        script.append("\r\n");
+        script.append("for (var i = 0; i < videos.length; i++) {");
+        script.append("\r\n");
+        script.append("videos[i].addEventListener('click', function() {");
+        script.append("\r\n");
+        script.append("// 获取当前视频的src属性");
+        script.append("\r\n");
+        script.append("var videoSrc = this.getAttribute('src');");
+        script.append("\r\n");
+        script.append("// 保存当前video元素的引用");
+        script.append("\r\n");
+        script.append("var videoElement = this;");
+        script.append("\r\n");
+        script.append("// 调用原生接口");
+        script.append("\r\n");
+        script.append("if (typeof Android !== 'undefined') {");
+        script.append("\r\n");
+        script.append("Android.playVideo(videoSrc);");
+        script.append("\r\n");
+        script.append("console.log('视频被点击了,调用原生播放!', videoSrc);");
+        script.append("\r\n");
+        script.append("} else {");
+        script.append("\r\n");
+        script.append("// 切换播放/暂停状态");
+        script.append("\r\n");
+        script.append("if (this.paused) {");
+        script.append("\r\n");
+        script.append("console.log('视频被点击了,使用web播放', videoSrc);");
+        script.append("\r\n");
+        script.append("this.play();");
+        script.append("\r\n");
+        script.append("} else {");
+        script.append("\r\n");
+        script.append("console.log('视频被点击了,使用web暂停', videoSrc);");
+        script.append("\r\n");
+        script.append("this.pause();");
+        script.append("\r\n");
+        script.append("}");
+        script.append("\r\n");
+        script.append("}");
+        script.append("\r\n");
+        script.append("// 添加点击反馈");
+        script.append("\r\n");
+        script.append("this.style.boxShadow = '0 0 10px rgba(0, 150, 255, 0.7)';");
+        script.append("\r\n");
+        script.append("setTimeout(function() {");
+        script.append("\r\n");
+        script.append("videoElement.style.boxShadow = 'none';");
+        script.append("\r\n");
+        script.append("}, 300);");
+        script.append("\r\n");
+        script.append("});");
+        script.append("\r\n");
+        script.append("}");
+        script.append("\r\n");
+        script.append("});");
+        script.append("\r\n");
+
+        document.body().appendChild(script);
+
+        html = document.html();
+        Log.d("最终的html", html);
+        binding.webView.loadDataWithBaseURL(
+                null,
+                html,
+                "text/html",
+                "utf-8",
+                null
+        );
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        binding.webView.onResume();
+        binding.webView.resumeTimers();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        binding.webView.onPause();
+        binding.webView.pauseTimers();
+    }
+
+    @Override
+    protected void onDestroy() {
+        Fragment fragment = getSupportFragmentManager().findFragmentByTag("video_dialog");
+        if (fragment instanceof VideoDialogFragment) {
+            ((VideoDialogFragment) fragment).dismissAllowingStateLoss();
+        }
+        binding.webView.stopLoading();
+        binding.webView.setWebViewClient(null);
+        binding.webView.setWebChromeClient(null);
+        binding.webView.destroy();
+        super.onDestroy();
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void onMessageEvent(PlayVideoEvent playVideoEvent) {
+        VideoDialogFragment videoDialogFragment = VideoDialogFragment.newInstance(playVideoEvent.url, "视频观看");
+        videoDialogFragment.show(getSupportFragmentManager(), "video_dialog");
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        EventBus.getDefault().register(this); // 注册
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        EventBus.getDefault().unregister(this); // 注销
+    }
+
+    private void initWeb() {
+        // 在WebView初始化前设置
+        WebView.setWebContentsDebuggingEnabled(true);
+        // 某些设备上可以尝试这个hack
+        System.setProperty("media.stagefright.force-software", "1");
+        setContentView(binding.getRoot());
+        WebSettings settings = binding.webView.getSettings();
+        // 基础功能
+        settings.setJavaScriptEnabled(true); // 启用JS
+        settings.setDomStorageEnabled(true); // 启用DOM存储
+        settings.setDatabaseEnabled(true); // 启用数据库
+        settings.setCacheMode(WebSettings.LOAD_NO_CACHE); // 缓存模式
+        // 视口设置
+        settings.setUseWideViewPort(true); // 使用宽视口
+        settings.setLoadWithOverviewMode(true); // 缩放至屏幕宽度
+        // 缩放控制
+        settings.setSupportZoom(true); // 支持缩放
+        settings.setBuiltInZoomControls(false); // 显示缩放控件
+        settings.setDisplayZoomControls(false); // 隐藏默认缩放按钮
+        // 其他设置
+        settings.setAllowFileAccess(true); // 允许访问文件
+        settings.setAllowContentAccess(true); // 允许内容访问
+        settings.setAllowFileAccessFromFileURLs(true); // 允许文件URL访问
+        settings.setAllowUniversalAccessFromFileURLs(true); // 允许跨域访问
+
+        settings.setMediaPlaybackRequiresUserGesture(false); // 允许自动播放
+        settings.setPluginState(WebSettings.PluginState.ON); // 启用插件
+        settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+        binding.webView.addJavascriptInterface(new WebAppInterface(), "Android");
+    }
+}

+ 42 - 0
app/src/main/java/xn/xxp/main/risk/RiskListAdapter.java

@@ -0,0 +1,42 @@
+package xn.xxp.main.risk;
+
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.viewholder.BaseViewHolder;
+
+import java.util.List;
+
+import http.vo.response.LabHazardVo;
+import xn.xxp.R;
+
+public class RiskListAdapter extends BaseQuickAdapter<LabHazardVo, BaseViewHolder> {
+    private String selectedItemId;  // 当前选中项的ID
+
+    public RiskListAdapter(List<LabHazardVo> data) {
+        super(R.layout.item_risk, data);
+    }
+
+    public void setSelectedItemId(String id) {
+        this.selectedItemId = id;
+        notifyDataSetChanged();  // 刷新列表显示状态
+    }
+
+    @Override
+    protected void convert(@NonNull BaseViewHolder holder, LabHazardVo item) {
+        TextView textView = (TextView) holder.itemView;
+        // 显示编号和中文名称
+        textView.setText(String.format("%s %s",
+                item.hazardCode,
+                item.chName
+        ));
+
+        // 根据选中状态设置背景
+        int bgRes = item.hazardId.equals(selectedItemId)
+                ? R.drawable.bg_item_checked
+                : R.drawable.bg_item_normal;
+        textView.setBackgroundResource(bgRes);
+    }
+}

+ 263 - 0
app/src/main/java/xn/xxp/main/risk/VideoDialogFragment.java

@@ -0,0 +1,263 @@
+package xn.xxp.main.risk;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+
+import com.shuyu.gsyvideoplayer.GSYVideoManager;
+import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder;
+import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack;
+import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer;
+
+import java.lang.ref.WeakReference;
+
+import xn.xxp.R;
+
+/**
+ * 全屏视频播放对话框Fragment
+ * 功能特性:
+ * 1. 基于DialogFragment实现,自动管理生命周期
+ * 2. 集成GSYVideoPlayer实现视频播放功能
+ * 3. 支持单例复用,避免重复创建
+ * 4. 自动处理Activity销毁时的资源释放
+ * 5. 支持全屏播放和手势控制
+ * 注意事项:
+ * - 必须添加网络权限和视频缓存权限
+ * - 建议在Manifest中配置configChanges防止旋转黑屏
+ * - 需依赖GSYVideoPlayer库
+ */
+public class VideoDialogFragment extends DialogFragment {
+
+    // region 成员变量
+    private StandardGSYVideoPlayer videoPlayer; // 视频播放器核心组件
+    private String videoUrl;                    // 当前播放视频URL
+    private String videoTitle;                  // 视频标题
+    private LifecycleObserver lifecycleObserver;// Activity生命周期观察者
+    // endregion
+
+    // region 单例管理
+    /**
+     * 使用弱引用管理实例,防止内存泄漏
+     * WeakReference在内存不足时会被GC回收,避免DialogFragment无法释放
+     */
+    private static WeakReference<VideoDialogFragment> weakInstance;
+
+    /**
+     * 获取对话框实例(单例模式)
+     *
+     * @param url   视频播放地址
+     * @param title 视频标题
+     * @return 已配置好的对话框实例
+     */
+    public static VideoDialogFragment newInstance(String url, String title) {
+        // 尝试从弱引用获取现有实例
+        VideoDialogFragment instance = (weakInstance != null) ? weakInstance.get() : null;
+
+        if (instance == null) {
+            instance = new VideoDialogFragment();
+            weakInstance = new WeakReference<>(instance);
+        }
+        instance.setVideoData(url, title); // 更新数据
+        return instance;
+    }
+    // endregion
+
+    // region 数据配置
+
+    /**
+     * 设置视频数据(用于实例复用)
+     *
+     * @param url   视频地址
+     * @param title 视频标题
+     */
+    private void setVideoData(String url, String title) {
+        this.videoUrl = url;
+        this.videoTitle = title;
+    }
+    // endregion
+
+    // region 生命周期管理
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // 设置对话框样式为全屏
+        setStyle(DialogFragment.STYLE_NORMAL, R.style.FullScreenDialog);
+    }
+
+    @NonNull
+    @Override
+    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+        // 创建无标题栏对话框
+        Dialog dialog = super.onCreateDialog(savedInstanceState);
+        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+        return dialog;
+    }
+
+    /**
+     * 绑定到Activity时注册生命周期监听
+     * 用于在Activity销毁时自动关闭对话框
+     */
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+
+        if (context instanceof FragmentActivity) {
+            FragmentActivity activity = (FragmentActivity) context;
+
+            // 创建生命周期观察者
+            lifecycleObserver = new LifecycleObserver() {
+                /**
+                 * 监听Activity销毁事件
+                 * 当宿主Activity被销毁时自动关闭对话框
+                 */
+                @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+                void onActivityDestroy() {
+                    if (isAdded()) { // 检查Fragment是否已添加
+                        dismissAllowingStateLoss(); // 允许状态丢失的关闭方式
+                    }
+                }
+            };
+
+            // 注册观察者
+            activity.getLifecycle().addObserver(lifecycleObserver);
+        }
+    }
+
+    /**
+     * 创建视图时初始化播放器
+     */
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater,
+                             @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        View root = inflater.inflate(R.layout.dialog_video, container, false);
+        initVideoPlayer(root); // 初始化播放器
+        return root;
+    }
+
+    /**
+     * 恢复时重启视频播放
+     */
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (videoPlayer != null) {
+            videoPlayer.onVideoResume(); // GSYVideoPlayer的恢复方法
+        }
+    }
+
+    /**
+     * 暂停时停止视频播放
+     */
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (videoPlayer != null) {
+            videoPlayer.onVideoPause(); // GSYVideoPlayer的暂停方法
+        }
+    }
+
+    /**
+     * 视图销毁时释放播放器资源
+     */
+    @Override
+    public void onDestroyView() {
+        releasePlayer();
+        super.onDestroyView();
+    }
+
+    /**
+     * 解绑时移除生命周期观察者
+     */
+    @Override
+    public void onDetach() {
+        if (getActivity() != null && lifecycleObserver != null) {
+            getActivity().getLifecycle().removeObserver(lifecycleObserver);
+        }
+        super.onDetach();
+    }
+
+    /**
+     * 完全销毁时清理静态引用
+     */
+    @Override
+    public void onDestroy() {
+        releasePlayer();
+        if (weakInstance != null) {
+            weakInstance.clear(); // 清除弱引用
+        }
+        super.onDestroy();
+    }
+    // endregion
+
+    // region 播放器管理
+
+    /**
+     * 初始化视频播放器
+     *
+     * @param root 根视图
+     */
+    private void initVideoPlayer(View root) {
+        videoPlayer = root.findViewById(R.id.video_player);
+
+        // 配置播放器选项
+        GSYVideoOptionBuilder gsyOption = new GSYVideoOptionBuilder();
+        gsyOption.setIsTouchWiget(true)        // 启用触摸手势
+                .setNeedShowWifiTip(false)     // 是否需要显示流量提示,默认true
+                .setRotateViewAuto(false)      // 禁用自动旋转
+                .setLockLand(false)            // 禁用横屏锁定
+                .setAutoFullWithSize(true)     // 根据尺寸自动全屏
+                .setShowFullAnimation(false)   // 禁用全屏动画
+                .setNeedLockFull(true)         // 需要全屏锁定
+                .setUrl(videoUrl)              // 设置视频地址
+                .setCacheWithPlay(false)        // 边播边缓存
+
+                .setVideoTitle(videoTitle)     // 设置视频标题
+                .setVideoAllCallBack(new GSYSampleCallBack() {
+                    /**
+                     * 视频播放完成回调
+                     */
+                    @Override
+                    public void onAutoComplete(String url, Object... objects) {
+                        super.onAutoComplete(url, objects);
+                        dismiss(); // 自动关闭对话框
+                    }
+                })
+                .build(videoPlayer);
+
+        // 自定义播放器UI
+        videoPlayer.getFullscreenButton().setVisibility(View.GONE); // 隐藏全屏按钮
+        videoPlayer.getBackButton().setVisibility(View.VISIBLE);    // 显示返回按钮
+        videoPlayer.getBackButton().setOnClickListener(v -> dismiss());
+        // 开始播放逻辑
+        videoPlayer.startPlayLogic();
+    }
+
+    /**
+     * 释放播放器资源
+     */
+    private void releasePlayer() {
+        if (videoPlayer != null) {
+            videoPlayer.release(); // 释放播放器
+
+            // 清除视频缓存(可选配置)
+            GSYVideoManager.instance().clearAllDefaultCache(getContext());
+
+            videoPlayer = null; // 置空防止内存泄漏
+        }
+    }
+    // endregion
+}

+ 13 - 0
app/src/main/java/xn/xxp/main/risk/WebAppInterface.java

@@ -0,0 +1,13 @@
+package xn.xxp.main.risk;
+
+import android.webkit.JavascriptInterface;
+
+import org.greenrobot.eventbus.EventBus;
+
+public class WebAppInterface {
+
+    @JavascriptInterface
+    public void playVideo(String url) {
+        EventBus.getDefault().post(new PlayVideoEvent(url));
+    }
+}

+ 170 - 0
app/src/main/java/xn/xxp/main/rule/RuleActivity.java

@@ -0,0 +1,170 @@
+package xn.xxp.main.rule;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import com.blankj.utilcode.util.LogUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import core.ui.activity.BaseCountDownActivity;
+import core.util.FastClickDelegate;
+import http.client.ApiRepository;
+import http.vo.request.SafetyListReq;
+import http.vo.response.SafeBook;
+import io.reactivex.rxjava3.functions.Consumer;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
+import xn.xxp.databinding.ActivityRuleBinding;
+import xn.xxp.widget.ITitleBar;
+import xn.xxp.widget.NavViewCompat;
+
+public class RuleActivity extends BaseCountDownActivity<ActivityRuleBinding> {
+
+    private ActivityRuleBinding binding;
+
+    private String mSystemFlag = "1";
+    private RuleAdapter ruleAdapter;
+    private List<SafeBook> safeBookList = new ArrayList<>();
+
+    @Override
+    public ITitleBar getMTitleBar() {
+        return binding.titleBar;
+    }
+
+    @Override
+    public NavViewCompat getMNavView() {
+        return binding.navView;
+    }
+
+    @Override
+    protected ActivityRuleBinding createViewBinding() {
+        return binding = ActivityRuleBinding.inflate(getLayoutInflater());
+    }
+
+    @Override
+    protected void initViews(Bundle savedInstanceState) {
+        super.initViews(savedInstanceState);
+        // 学校制度
+        binding.schoolSystem.setOnClickListener(new FastClickDelegate(new Function1<View, Unit>() {
+            @Override
+            public Unit invoke(View view) {
+                mSystemFlag = "1";
+                load();
+                return null;
+            }
+        }));
+        // 学院制度
+        binding.collegeSystem.setOnClickListener(new FastClickDelegate(new Function1<View, Unit>() {
+            @Override
+            public Unit invoke(View view) {
+                mSystemFlag = "2";
+                load();
+                return null;
+            }
+        }));
+        // 中心制度
+        binding.schoolSystem.setOnClickListener(new FastClickDelegate(new Function1<View, Unit>() {
+            @Override
+            public Unit invoke(View view) {
+                mSystemFlag = "3";
+                load();
+                return null;
+            }
+        }));
+
+        binding.swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                queryData();
+
+            }
+        });
+        ruleAdapter = new RuleAdapter(safeBookList);
+        binding.listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                SafeBook safeBook = ruleAdapter.getItem(position);
+                if (TextUtils.isEmpty(safeBook.content)) {
+                    showToast("数据异常,无法展示,请重新上传!");
+                    return;
+                }
+                Intent intent = new Intent(RuleActivity.this, RuleDetailActivity.class);
+                intent.putExtra("data", safeBook);
+                intent.putExtra("onEboard", true);
+                startActivity(intent);
+            }
+        });
+
+        binding.listView.setAdapter(ruleAdapter);
+
+        binding.swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                queryData();
+            }
+        });
+
+    }
+
+    @Override
+    protected void initData() {
+        super.initData();
+        load();
+    }
+
+    private void load() {
+        binding.swipeRefreshLayout.setRefreshing(true);
+        queryData();
+    }
+
+    private void queryData() {
+        SafetyListReq safetyListReq = new SafetyListReq();
+        safetyListReq.type = mSystemFlag;
+        LogUtils.json(safetyListReq);
+        addDisposable(ApiRepository.INSTANCE.safeBookList(safetyListReq).subscribe(new Consumer<List<SafeBook>>() {
+            @Override
+            public void accept(List<SafeBook> safeBooks) throws Throwable {
+                dispatchLoadDataSuccess(safeBooks);
+            }
+        }, new Consumer<Throwable>() {
+            @Override
+            public void accept(Throwable throwable) throws Throwable {
+                LogUtils.e(Log.getStackTraceString(throwable));
+                dispatchLoadDataFailure(throwable);
+            }
+        }));
+    }
+
+    private void dispatchLoadDataFailure(Throwable throwable) {
+        LogUtils.e(Log.getStackTraceString(throwable));
+        if (!isDestroyed()) {
+            binding.swipeRefreshLayout.setRefreshing(false);
+            showNetError(throwable);
+        }
+    }
+
+    private void dispatchLoadDataSuccess(List<SafeBook> safeBooks) {
+        if (!isDestroyed()) {
+            binding.swipeRefreshLayout.setRefreshing(false);
+            if (null != safeBooks && !safeBooks.isEmpty()) {
+                safeBookList.clear();
+                safeBookList.addAll(safeBooks);
+                ruleAdapter.notifyDataSetChanged();
+            }
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        binding.swipeRefreshLayout.setRefreshing(false);
+        super.onDestroy();
+    }
+}

+ 61 - 0
app/src/main/java/xn/xxp/main/rule/RuleAdapter.java

@@ -0,0 +1,61 @@
+package xn.xxp.main.rule;
+
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import java.util.List;
+
+import http.vo.response.SafeBook;
+import xn.xxp.databinding.ItemRuleBinding;
+
+public class RuleAdapter extends BaseAdapter {
+    private List<SafeBook> safeBookList;
+
+    public RuleAdapter(List<SafeBook> safeBookList) {
+        this.safeBookList = safeBookList;
+    }
+
+    @Override
+    public int getCount() {
+        return null == safeBookList || safeBookList.isEmpty() ? 0 : safeBookList.size();
+    }
+
+    @Override
+    public SafeBook getItem(int position) {
+        return null != safeBookList && !safeBookList.isEmpty() ? safeBookList.get(position) : null;
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return 0;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ViewHolder viewHolder;
+        if (convertView == null) {
+            ItemRuleBinding binding = ItemRuleBinding.inflate(LayoutInflater.from(parent.getContext()));
+            convertView = binding.getRoot();
+            viewHolder = new ViewHolder(binding);
+            convertView.setTag(viewHolder);
+        } else {
+            viewHolder = (ViewHolder) convertView.getTag();
+        }
+        SafeBook safeBook = getItem(position);
+        if (null != safeBook) {
+            viewHolder.binding.text.setText(TextUtils.isEmpty(safeBook.name) ? "" : safeBook.name);
+        }
+        return convertView;
+    }
+
+    static class ViewHolder {
+        private final ItemRuleBinding binding;
+
+        public ViewHolder(ItemRuleBinding binding) {
+            this.binding = binding;
+        }
+    }
+}

+ 167 - 0
app/src/main/java/xn/xxp/main/rule/RuleDetailActivity.java

@@ -0,0 +1,167 @@
+package xn.xxp.main.rule;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.bumptech.glide.Glide;
+import com.github.barteksc.pdfviewer.scroll.DefaultScrollHandle;
+
+import core.ui.activity.BaseCountDownActivity;
+import core.util.FastClickDelegate;
+import http.net.DownloadHelper;
+import http.vo.response.SafeBook;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
+import xn.xxp.R;
+import xn.xxp.databinding.ActivityRuleDetailBinding;
+import xn.xxp.utils.QrTool;
+import xn.xxp.utils.Tool;
+import xn.xxp.widget.ITitleBar;
+import xn.xxp.widget.NavViewCompat;
+
+public class RuleDetailActivity extends BaseCountDownActivity<ActivityRuleDetailBinding> {
+
+    private ActivityRuleDetailBinding binding;
+    private SafeBook safeBook;
+    private DownloadHelper downloadHelper;
+
+    @Override
+    public ITitleBar getMTitleBar() {
+        return null;
+    }
+
+    @Override
+    public NavViewCompat getMNavView() {
+        return null;
+    }
+
+    @Override
+    protected ActivityRuleDetailBinding createViewBinding() {
+        return binding = ActivityRuleDetailBinding.inflate(getLayoutInflater());
+    }
+
+    @Override
+    protected void initViews(Bundle savedInstanceState) {
+        super.initViews(savedInstanceState);
+        Intent getIntent = getIntent();
+        if (null != getIntent) {
+            safeBook = (SafeBook) getIntent.getSerializableExtra("data");
+        }
+
+        binding.homeLayout.setOnClickListener(new FastClickDelegate(new Function1<View, Unit>() {
+            @Override
+            public Unit invoke(View view) {
+                logoutCountDownFinish();
+                return null;
+            }
+        }));
+        binding.backLayout.setOnClickListener(new FastClickDelegate(new Function1<View, Unit>() {
+            @Override
+            public Unit invoke(View view) {
+                finish();
+                return null;
+            }
+        }));
+
+    }
+
+    @Override
+    protected void initData() {
+        super.initData();
+        binding.titleText.setText(safeBook.name);
+        showLoading("加载中...");
+        downloadHelper = new DownloadHelper(this);
+        downloadHelper.startDownload(safeBook.content, "pdf.pdf", 3399, new DownloadHelper.DownloadCallback() {
+            @Override
+            public void onProgress(int progress, long downloadedBytes, long totalBytes) {
+
+            }
+
+            @Override
+            public void onSuccess(Uri fileUri) {
+                dismissLoading();
+                try {
+                    binding.pdfView.fromUri(fileUri)
+                            .defaultPage(0) // 默认显示第一页
+                            .enableSwipe(true) // 允许滑动翻页
+                            .swipeHorizontal(false) // 垂直滑动
+                            .enableDoubletap(true) // 双击缩放
+                            .onLoad(nbPages -> {
+                                // 加载完成回调
+                                showToast("加载完成!");
+                            })
+                            .onPageScroll((page, positionOffset) -> {
+                                // 页面滚动监听
+                            })
+                            .onError(error -> { // 加载错误处理
+                                showToast("加载错误:" + error.getMessage());
+                                LogUtils.e(Log.getStackTraceString(error));
+                            })
+                            .enableAnnotationRendering(true) // 渲染注释
+                            .password(null) // PDF密码(如果有)
+                            .scrollHandle(new DefaultScrollHandle(RuleDetailActivity.this)) // 滚动条
+                            .load();
+                } catch (Exception e) {
+                    LogUtils.e(Log.getStackTraceString(e));
+                    showNetError(e);
+                }
+
+            }
+
+            @Override
+            public void onFailure(int reason) {
+                showToast("下载文件异常:" + reason + ",请重新上传文件!");
+                dismissLoading();
+            }
+
+            @Override
+            public void onPaused() {
+
+            }
+
+            @Override
+            public void onCancelled() {
+
+            }
+        });
+        ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Bitmap>() {
+            @Override
+            public Bitmap doInBackground() throws Throwable {
+                return QrTool.generateQRCode(safeBook.realContent, Tool.INSTANCE.dip2px(150f));
+            }
+
+            @Override
+            public void onSuccess(Bitmap result) {
+                Glide.with(binding.qrCode).asBitmap().load(result).error(R.mipmap.img_error).into(binding.qrCode);
+            }
+        });
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        downloadHelper.cancelDownload(3399);
+        downloadHelper.release();
+        super.onDestroy();
+    }
+
+    @Override
+    protected boolean enabledBackCountDown() {
+        return true;
+    }
+}

+ 1 - 0
app/src/main/java/xn/xxp/mqtt/MqttManager.kt

@@ -61,6 +61,7 @@ class MqttManager private constructor() {
         mMqttWorker.setListener(object : MqttWorker.Listener {
 
             override fun onConnectSuccess() {
+                LogUtils.d("MQTT连接成功")
                 //  连接成功后,订阅主题
                 val topics = arrayOf(
                     Constants.MqttConfig.Topic.MIDDLE_INFO.plus(labConfig.labId),

+ 17 - 0
app/src/main/java/xn/xxp/receiver/TimeTickReceiver.java

@@ -4,6 +4,11 @@ import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+
+import xn.xxp.app.SyncFaceTool;
+import xn.xxp.room.RoomTool;
 import xn.xxp.utils.Tool;
 
 public class TimeTickReceiver extends BroadcastReceiver {
@@ -14,5 +19,17 @@ public class TimeTickReceiver extends BroadcastReceiver {
         intent.putExtra("heartbeat", "heartbeat");
         context.sendBroadcast(newIntent);
         Tool.INSTANCE.startTaskService();
+        ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
+            @Override
+            public Object doInBackground() throws Throwable {
+                SyncFaceTool.INSTANCE.doWork();
+                return null;
+            }
+
+            @Override
+            public void onSuccess(Object result) {
+                LogUtils.json(RoomTool.getInstance().faceDao().getAll());
+            }
+        });
     }
 }

+ 1 - 1
app/src/main/java/xn/xxp/room/bean/LabConfig.java

@@ -11,7 +11,7 @@ public class LabConfig {
     private int logoutTime; // 自动注销时间 秒
     private int returnTime; // 自动返回时间 秒
     private int authType; // 身份核验方式 1-指纹 2-人脸/指纹/刷卡/密码 3-人脸/指纹 4-刷卡+密码 5-人脸+密码 6-人脸+刷卡
-    private String isRelationGuard=""; // 是否关联门禁  1 关联  2不关联
+    private String isRelationGuard = ""; // 是否关联门禁  1 关联  2不关联
 
     public long getLabId() {
         return labId;

+ 1 - 1
app/src/main/java/xn/xxp/utils/Constants.kt

@@ -29,7 +29,7 @@ object Constants {
             // 物联控制topic
             const val THINGS = "iot/hardware/all/sub/"
 
-            // 电子信息牌登录方式变更
+            // 实验室配置变更
             const val LAB_INFO = "lab/subInfo"
 
             //  远程开锁

+ 4 - 0
app/src/main/java/xn/xxp/utils/Tool.java

@@ -46,6 +46,10 @@ public enum Tool {
         cmd("pm grant " + AppUtils.getAppPackageName() + " android.permission.RECORD_AUDIO");
     }
 
+    public void openAdb() {
+        InfoSystemApi.openAdb();
+    }
+
     /**
      * 保活updateApp
      */

+ 7 - 0
app/src/main/res/anim/slide_in_bottom.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:duration="300"
+        android:fromYDelta="100%"
+        android:toYDelta="0%"/>
+</set>

+ 7 - 0
app/src/main/res/anim/slide_out_bottom.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:duration="300"
+        android:fromYDelta="0%"
+        android:toYDelta="100%"/>
+</set>

+ 11 - 0
app/src/main/res/layout/activity_browser.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <WebView
+        android:id="@+id/webView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</RelativeLayout>

+ 1 - 2
app/src/main/res/layout/activity_main.xml

@@ -274,8 +274,7 @@
             android:background="@mipmap/icon_sy_cgqbg"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toStartOf="@id/back"
-            app:layout_constraintStart_toStartOf="parent"
-            />
+            app:layout_constraintStart_toStartOf="parent" />
 
         <xn.xxp.widget.LooperRecyclerView
             android:id="@+id/bulletinBoardView"

+ 3 - 1
app/src/main/res/layout/activity_risk.xml

@@ -108,8 +108,10 @@
                 android:background="@android:color/transparent"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintHorizontal_bias="0.0"
                 app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toTopOf="parent" />
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintVertical_bias="0.0" />
 
             <LinearLayout
                 android:id="@+id/fullScreen"

+ 189 - 0
app/src/main/res/layout/activity_risk_list.xml

@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout 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"
+    android:background="@mipmap/img_bg"
+    android:orientation="vertical">
+
+    <xn.xxp.widget.TitleBar
+        android:id="@+id/titleBar"
+        android:layout_width="0dp"
+        android:layout_height="56dp"
+        android:background="@mipmap/img_navigation_bg"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:showNotice="true"
+        app:signed="true" />
+
+    <xn.xxp.widget.NavViewCompat
+        android:id="@+id/nav_view"
+        android:layout_width="0dp"
+        android:layout_height="42dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/titleBar"
+        app:location="危险源"
+        app:showBack="true"
+        app:showHome="true" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_marginStart="17dp"
+        android:layout_marginTop="8dp"
+        android:layout_marginEnd="17dp"
+        android:layout_marginBottom="16dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/nav_view">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/left_nav"
+            android:layout_width="184dp"
+            android:layout_height="match_parent"
+            android:background="@drawable/shape_rect_round_11_solid"
+            android:orientation="vertical"
+            android:padding="12dp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <ImageView
+                android:id="@+id/sdms_img"
+                android:layout_width="27dp"
+                android:layout_height="27dp"
+                android:layout_marginStart="15dp"
+                android:layout_marginTop="5dp"
+                android:contentDescription="@null"
+                android:src="@mipmap/icon_jssm_wxy"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="13dp"
+                android:text="危险源"
+                android:textColor="#ffffffff"
+                android:textSize="17sp"
+                app:layout_constraintBottom_toBottomOf="@id/sdms_img"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toEndOf="@id/sdms_img"
+                app:layout_constraintTop_toTopOf="@id/sdms_img" />
+
+            <androidx.recyclerview.widget.RecyclerView
+                android:id="@+id/risk"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_marginTop="14dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/sdms_img" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginStart="11dp"
+            android:background="@drawable/shape_rect_round_11_solid_white"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@id/left_nav"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <WebView
+                android:id="@+id/webView"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent" />
+
+            <LinearLayout
+                android:id="@+id/fullScreen"
+                android:layout_width="56dp"
+                android:layout_height="23dp"
+                android:background="@mipmap/bg_full_screen"
+                android:gravity="center_vertical"
+                android:orientation="horizontal"
+                app:layout_constraintStart_toEndOf="@id/webView"
+                app:layout_constraintTop_toTopOf="@id/webView">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="7dp"
+                    android:text="全屏"
+                    android:textColor="#333333"
+                    android:textSize="10sp"
+                    tools:ignore="SmallSp" />
+
+                <ImageView
+                    android:layout_width="11dp"
+                    android:layout_height="11dp"
+                    android:layout_marginStart="5dp"
+                    android:contentDescription="@null"
+                    android:src="@mipmap/icon_full_screen" />
+
+            </LinearLayout>
+
+            <ProgressBar
+                android:id="@+id/progressbar"
+                style="@android:style/Widget.ProgressBar.Horizontal"
+                android:layout_width="0dp"
+                android:layout_height="2dp"
+                android:progress="0"
+                android:progressDrawable="@drawable/progressbar"
+                app:layout_constraintEnd_toEndOf="@id/webView"
+                app:layout_constraintStart_toStartOf="@id/webView"
+                app:layout_constraintTop_toTopOf="@id/webView" />
+
+            <TextView
+                android:id="@+id/qr_tip"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="20dp"
+                android:layout_marginBottom="17dp"
+                android:text="微信扫描二维码查看"
+                android:textColor="#ff333333"
+                android:textSize="10sp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                tools:ignore="SmallSp" />
+
+            <ImageView
+                android:id="@+id/qrCode"
+                android:layout_width="79dp"
+                android:layout_height="79dp"
+                android:layout_marginBottom="9dp"
+                android:contentDescription="@null"
+                android:scaleType="fitXY"
+                app:layout_constraintBottom_toTopOf="@id/qr_tip"
+                app:layout_constraintEnd_toEndOf="@id/qr_tip"
+                app:layout_constraintStart_toStartOf="@id/qr_tip"
+                tools:src="@mipmap/img_error" />
+
+            <androidx.constraintlayout.widget.Group
+                android:id="@+id/qrGroup"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="invisible"
+                app:constraint_referenced_ids="qr_tip,qrCode,fullScreen" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <FrameLayout
+        android:id="@+id/videoFullScreen"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 133 - 0
app/src/main/res/layout/activity_rule.xml

@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout 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"
+    android:background="@mipmap/img_bg"
+    android:orientation="vertical">
+
+    <xn.xxp.widget.TitleBar
+        android:id="@+id/titleBar"
+        android:layout_width="0dp"
+        android:layout_height="56dp"
+        android:background="@mipmap/img_navigation_bg"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:showNotice="true"
+        app:signed="true" />
+
+    <xn.xxp.widget.NavViewCompat
+        android:id="@+id/nav_view"
+        android:layout_width="0dp"
+        android:layout_height="42dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/titleBar"
+        app:location="规章制度"
+        app:showBack="true"
+        app:showHome="true" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginStart="17dp"
+        android:layout_marginTop="7dp"
+        android:layout_marginEnd="17dp"
+        android:layout_marginBottom="50dp"
+        android:background="@drawable/shape_rect_round_11_solid_white_20p"
+        android:padding="17dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/nav_view">
+
+        <RadioGroup
+            android:id="@+id/left_nav"
+            android:layout_width="184dp"
+            android:layout_height="match_parent"
+            android:background="@drawable/shape_rect_round_11_solid"
+            android:divider="@drawable/divider_white"
+            android:orientation="vertical"
+            android:paddingStart="10dp"
+            android:paddingTop="12dp"
+            android:paddingEnd="10dp"
+            android:paddingBottom="12dp"
+            android:showDividers="middle|end"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <RadioButton
+                android:id="@+id/schoolSystem"
+                android:layout_width="match_parent"
+                android:layout_height="56dp"
+                android:background="@drawable/bg_item_selectable"
+                android:button="@null"
+                android:checked="true"
+                android:drawableStart="@mipmap/icon_aqzd_xxzd"
+                android:drawablePadding="20dp"
+                android:paddingStart="18dp"
+                android:text="学校制度"
+                android:textColor="@android:color/white"
+                android:textSize="16sp" />
+
+            <RadioButton
+                android:id="@+id/collegeSystem"
+                android:layout_width="match_parent"
+                android:layout_height="56dp"
+                android:background="@drawable/bg_item_selectable"
+                android:button="@null"
+                android:checked="false"
+                android:drawableStart="@mipmap/icon_aqzd_xyzd"
+                android:drawablePadding="20dp"
+                android:paddingStart="18dp"
+                android:text="学院制度"
+                android:textColor="@android:color/white"
+                android:textSize="16sp" />
+
+            <RadioButton
+                android:id="@+id/centerSystem"
+                android:layout_width="match_parent"
+                android:layout_height="56dp"
+                android:background="@drawable/bg_item_selectable"
+                android:button="@null"
+                android:checked="false"
+                android:drawableStart="@mipmap/icon_aqzd_zxzd"
+                android:drawablePadding="20dp"
+                android:paddingStart="18dp"
+                android:text="中心制度"
+                android:textColor="@android:color/white"
+                android:textSize="16sp" />
+
+        </RadioGroup>
+
+        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+            android:id="@+id/swipeRefreshLayout"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginStart="11dp"
+            android:background="@drawable/shape_rect_round_11_solid_white"
+            android:paddingStart="15dp"
+            android:paddingTop="17dp"
+            android:paddingEnd="15dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@id/left_nav"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <ListView
+                android:id="@+id/listView"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_marginTop="12dp"
+                android:layout_marginBottom="12dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 162 - 0
app/src/main/res/layout/activity_rule_detail.xml

@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout 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"
+    android:background="@mipmap/img_bg">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/titleBar"
+        android:layout_width="0dp"
+        android:layout_height="56dp"
+        android:background="@mipmap/img_navigation_bg"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <TextView
+            android:id="@+id/titleText"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="33dp"
+            android:layout_marginEnd="10dp"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textColor="@android:color/white"
+            android:textSize="16sp"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/homeLayout"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:text="文件名称" />
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/backLayout"
+            android:layout_width="84dp"
+            android:layout_height="28dp"
+            android:layout_marginEnd="17dp"
+            android:background="@drawable/shape_rect_round_5_solid_0183fa_10p"
+            android:gravity="center_vertical"
+            android:padding="5dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <ImageView
+                android:id="@+id/backImage"
+                android:layout_width="18dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="3dp"
+                android:contentDescription="@null"
+                android:scaleType="centerCrop"
+                android:src="@mipmap/icon_sys_return"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/back"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="7dp"
+                android:text="返回"
+                android:textColor="@android:color/white"
+                android:textSize="10sp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toEndOf="@id/backImage"
+                app:layout_constraintTop_toTopOf="parent"
+                tools:ignore="SmallSp" />
+
+            <TextView
+                android:id="@+id/countDown"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="@android:color/white"
+                android:textSize="8sp"
+                app:layout_constraintBaseline_toBaselineOf="@id/back"
+                app:layout_constraintStart_toEndOf="@id/back"
+                tools:ignore="SmallSp" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/homeLayout"
+            android:layout_width="73dp"
+            android:layout_height="28dp"
+            android:layout_marginEnd="6dp"
+            android:background="@drawable/shape_rect_round_5_solid_0183fa_10p"
+            android:gravity="center_vertical"
+            android:padding="5dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/backLayout"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <ImageView
+                android:id="@+id/homeImage"
+                android:layout_width="18dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="3dp"
+                android:contentDescription="@null"
+                android:scaleType="centerCrop"
+                android:src="@mipmap/icon_sys_sy"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/home"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="13dp"
+                android:text="首页"
+                android:textColor="@android:color/white"
+                android:textSize="10sp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toEndOf="@id/homeImage"
+                app:layout_constraintTop_toTopOf="parent"
+                tools:ignore="SmallSp" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <com.github.barteksc.pdfviewer.PDFView
+        android:id="@+id/pdfView"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginStart="120dp"
+        android:layout_marginEnd="120dp"
+        android:background="#E0E0E0"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/titleBar" />
+
+    <TextView
+        android:id="@+id/qr_tip"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="20dp"
+        android:layout_marginBottom="17dp"
+        android:text="微信扫描二维码查看"
+        android:textColor="#ff333333"
+        android:textSize="10sp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        tools:ignore="SmallSp" />
+
+    <ImageView
+        android:id="@+id/qrCode"
+        android:layout_width="79dp"
+        android:layout_height="79dp"
+        android:layout_marginBottom="9dp"
+        android:contentDescription="@null"
+        android:scaleType="fitXY"
+        app:layout_constraintBottom_toTopOf="@id/qr_tip"
+        app:layout_constraintEnd_toEndOf="@id/qr_tip"
+        app:layout_constraintStart_toStartOf="@id/qr_tip"
+        tools:src="@mipmap/img_error" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 12 - 0
app/src/main/res/layout/dialog_video.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
+        android:id="@+id/video_player"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:keepScreenOn="true" />
+
+</FrameLayout>

+ 29 - 0
app/src/main/res/layout/item_rule.xml

@@ -0,0 +1,29 @@
+<?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="45dp"
+    android:background="@android:color/white"
+    android:gravity="center_vertical"
+    android:paddingStart="10dp"
+    android:paddingEnd="10dp">
+
+    <ImageView
+        android:layout_width="27dp"
+        android:layout_height="23dp"
+        android:contentDescription="@null"
+        android:scaleType="centerInside"
+        android:src="@mipmap/icon_aqzd_wjm" />
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="20dp"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:textColor="#ff000000"
+        android:textSize="14sp"
+        tools:text="关于印发《中国矿业大学实验室安全责任追究办法(试行)》的通知" />
+
+</LinearLayout>

+ 0 - 1
app/src/main/res/layout/item_safe_classify.xml

@@ -11,7 +11,6 @@
         android:id="@+id/safe_classify_TV"
         android:layout_width="70px"
         android:layout_height="match_parent"
-        android:background="@color/colorAccent"
         android:gravity="center"
         android:maxLines="4"
         android:text="危险类别"

BIN
app/src/main/res/mipmap-xhdpi/icon_sy_hkm.webp


BIN
app/src/main/res/mipmap-xhdpi/img_bg_hkclb.webp


+ 15 - 0
app/src/main/res/values/styles.xml

@@ -10,6 +10,21 @@
         <item name="android:windowContentOverlay">@null</item>
     </style>
 
+    <style name="FullScreenDialog" parent="Theme.AppCompat.Dialog">
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowIsFloating">false</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:backgroundDimEnabled">true</item>
+        <!-- 自定义动画 -->
+        <item name="android:windowAnimationStyle">@style/DialogAnimation</item>
+    </style>
+
+    <style name="DialogAnimation">
+        <item name="android:windowEnterAnimation">@anim/slide_in_bottom</item>
+        <item name="android:windowExitAnimation">@anim/slide_out_bottom</item>
+    </style>
+
     <!-- popup window 显示消失动画-->
     <style name="PopWindowAnimStyle">
         <!-- 指定显示的动画xml  -->

+ 6 - 1
settings.gradle

@@ -10,7 +10,9 @@ pluginManagement {
         mavenCentral()
         gradlePluginPortal()
         maven { url 'https://jitpack.io' }
-
+        maven {
+            url "https://maven.aliyun.com/repository/public"
+        }
     }
 }
 dependencyResolutionManagement {
@@ -19,6 +21,9 @@ dependencyResolutionManagement {
         google()
         mavenCentral()
         maven { url 'https://jitpack.io' }
+        maven {
+            url "https://maven.aliyun.com/repository/public"
+        }
 
     }
 }