JaycePC пре 14 часа
родитељ
комит
e8e558a6ce

+ 1 - 0
app/build.gradle

@@ -136,4 +136,5 @@ dependencies {
     implementation libs.android.pdf.viewer
     // banner
     implementation 'com.github.zhpanvip:bannerviewpager:3.5.12'
+    implementation 'com.github.NodeMedia:NodeMediaClient-Android:2.9.23'
 }

+ 5 - 1
app/src/main/java/core/ui/activity/BaseActivity.java

@@ -11,6 +11,7 @@ import androidx.recyclerview.widget.RecyclerView;
 import androidx.viewbinding.ViewBinding;
 
 import com.blankj.utilcode.util.LogUtils;
+import com.hikvision.dmb.display.InfoDisplayApi;
 
 import core.ui.common.AbsUIDelegate;
 import core.ui.common.UIDelegateImpl;
@@ -27,8 +28,11 @@ public abstract class BaseActivity<VB extends ViewBinding> extends AppCompatActi
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
+        if(2!= InfoDisplayApi.getScreenRotate()){
+            InfoDisplayApi.setScreenRotate(2);
+        }
         super.onCreate(savedInstanceState);
-        ScreenAdapter.INSTANCE.setCustomDensity(this);
+        //ScreenAdapter.INSTANCE.setCustomDensity(this);
         uiDelegate = AbsUIDelegate.Companion.create();
         configImmersiveMode();
         binding = createViewBinding();

+ 3 - 3
app/src/main/java/xn/xxp/home/window/WindowFragment.java

@@ -98,8 +98,7 @@ public class WindowFragment extends RcBaseFragment<FragmentWindowBinding> {
     private void initLaboratoryInfo(LaboratoryVo lab) {
         // 实验室安全分级
         GradientDrawable dangerShapeDrawable = (GradientDrawable) ContextCompat.getDrawable(requireContext(), R.drawable.shape_rect_round_20px_ll);
-        dangerShapeDrawable.setColor(TextUtils.isEmpty(lab.dangerColor) ? Color.parseColor("#FF15CA48") : Color.parseColor(lab.dangerColor));
-        binding.dangerLL.setBackground(dangerShapeDrawable);
+        dangerShapeDrawable.setColor(TextUtils.isEmpty(lab.dangerColor) ? Color.parseColor("#FF15CA48") : Color.parseColor(lab.dangerColor));binding.dangerLL.setBackground(dangerShapeDrawable);
         binding.dangerLevelTV.setText(TextUtils.isEmpty(lab.dangerLevel) ? "安全分级" : lab.dangerLevel);
         binding.dangerTypeTV.setText(TextUtils.isEmpty(lab.dangerName) ? "安全分类" : lab.dangerName);
 
@@ -135,8 +134,9 @@ public class WindowFragment extends RcBaseFragment<FragmentWindowBinding> {
         }else if (TextUtils.isEmpty(lab.safetyLiaisonName) && TextUtils.isEmpty(lab.safetyLiaisonPhone)){
             safetyLiaisonPhone = "";
         }
+
         //安全联络员名字电话
-        binding.leaderPhoneTV.setText(safetyLiaisonPhone);
+        binding.leaderPhoneTV.setText(lab.safeUserDesc);
         //binding.safetyLiaisonNameTV.setText(TextUtils.isEmpty(lab.safetyLiaisonName) ? "" : lab.safetyLiaisonName);
         // 实验室二维码
         if (!TextUtils.isEmpty(lab.qrCodeUrl)) {

+ 448 - 95
app/src/main/java/xn/xxp/main/monitor/MonitorActivity.java

@@ -1,27 +1,31 @@
 package xn.xxp.main.monitor;
 
+import static com.blankj.utilcode.util.ViewUtils.runOnUiThread;
+
 import android.content.DialogInterface;
-import android.graphics.SurfaceTexture;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Log;
-import android.view.TextureView;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.widget.GridLayout;
+import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.fragment.app.Fragment;
 
 import com.blankj.utilcode.util.LogUtils;
 import com.blankj.utilcode.util.ThreadUtils;
-import com.hikvision.hatomplayer.DefaultHatomPlayer;
-import com.hikvision.hatomplayer.HatomPlayer;
-import com.hikvision.hatomplayer.PlayCallback;
-import com.hikvision.hatomplayer.PlayConfig;
-import com.hikvision.hatomplayer.core.Quality;
+import cn.nodemedia.NodePlayer;
+import cn.nodemedia.NodePlayerDelegate;
+import cn.nodemedia.NodePlayerView;
 
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import core.ui.activity.BaseCountDownActivity;
 import core.util.FastClickDelegate;
@@ -43,6 +47,9 @@ public class MonitorActivity extends BaseCountDownActivity<ActivityMonitorBindin
     private ActivityMonitorBinding binding;
     private List<MonitorInfo> monitorInfoList = new ArrayList<>();
     private MonitorDialog monitorDialog;
+    private volatile boolean isExiting = false;
+    private volatile boolean isDialogShowing = false;
+    private Handler mainHandler = new Handler(Looper.getMainLooper());
 
     @Override
     public ITitleBar getMTitleBar() {
@@ -70,28 +77,7 @@ public class MonitorActivity extends BaseCountDownActivity<ActivityMonitorBindin
 
             @Override
             public void onBackViewClicked() {
-                showLoading("正在退出...");
-                ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
-                    @Override
-                    public Object doInBackground() throws Throwable {
-                        if (null != monitorInfoList) {
-                            for (int i = 0; i < monitorInfoList.size(); i++) {
-                                MonitorInfo monitorInfo = monitorInfoList.get(i);
-                                HatomPlayer hatomPlayer = monitorInfo.getHatomPlayer();
-                                if (null != hatomPlayer) {
-                                    hatomPlayer.stop();
-                                }
-                            }
-                        }
-                        return null;
-                    }
-
-                    @Override
-                    public void onSuccess(Object result) {
-                        dismissLoading();
-                        finish();
-                    }
-                });
+                safeExitActivity();
             }
 
             @Override
@@ -100,9 +86,8 @@ public class MonitorActivity extends BaseCountDownActivity<ActivityMonitorBindin
             }
         });
 
-
         LabConfig labConfig = RoomTool.getInstance().labConfigDao().getLabConfig();
-        showLoading("加载...");
+        showLoading("加载监控...");
         addDisposable(ApiRepository.INSTANCE.cameraBySubjectId(String.valueOf(labConfig.getLabId()), LabApp.userVo.userId, LabApp.userVo.userName, 3).subscribe(new Consumer<List<MonitorVo>>() {
             @Override
             public void accept(List<MonitorVo> monitorVos) throws Throwable {
@@ -114,87 +99,172 @@ public class MonitorActivity extends BaseCountDownActivity<ActivityMonitorBindin
                             int width = binding.gridLayout.getWidth();
                             int height = binding.gridLayout.getHeight();
 
+                            // 计数器
+                            final AtomicInteger loadingCounter = new AtomicInteger(0);
+                            final int totalPlayers = monitorVos.size();
+
+                            // 立即关闭主loading
+                            dismissLoading();
+
                             for (int i = 0; i < monitorVos.size(); i++) {
                                 MonitorVo monitorVo = monitorVos.get(i);
                                 MonitorInfo monitorInfo = new MonitorInfo();
-                                TextureView textureView = new TextureView(MonitorActivity.this);
+
+                                // 创建 NodePlayerView
+                                NodePlayerView nodePlayerView = new NodePlayerView(MonitorActivity.this);
                                 GridLayout.LayoutParams params = new GridLayout.LayoutParams();
                                 params.width = width / 3;
                                 params.height = height / 3;
-                                params.setMargins(5, 5, 5, 5); // 设置间距
-                                textureView.setLayoutParams(params);
-                                textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
-                                    @Override
-                                    public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
-                                        for (int j = 0; j < monitorInfoList.size(); j++) {
-                                            MonitorInfo monitorInfo = monitorInfoList.get(j);
-                                            SurfaceTexture surfaceTexture = monitorInfo.getTextureView().getSurfaceTexture();
-                                            if (surfaceTexture == surface) {
-                                                play(monitorInfo.getHatomPlayer(), surface, monitorInfo.getMonitorVo().streamUrl);
-                                                break;
-                                            }
-                                        }
-                                    }
+                                params.setMargins(5, 5, 5, 5);
+                                nodePlayerView.setLayoutParams(params);
+
+                                // 加载提示
+                                TextView loadingText = new TextView(MonitorActivity.this);
+                                loadingText.setLayoutParams(new android.widget.FrameLayout.LayoutParams(
+                                        android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
+                                        android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
+                                        android.view.Gravity.CENTER
+                                ));
+                                loadingText.setText("连接中");
+                                loadingText.setTextColor(0xFFFFFFFF);
+                                loadingText.setTextSize(10);
+                                loadingText.setBackgroundColor(0x80000000);
+                                loadingText.setPadding(10, 5, 10, 5);
+                                loadingText.setVisibility(View.VISIBLE);
+
+                                // 容器布局
+                                android.widget.FrameLayout container = new android.widget.FrameLayout(MonitorActivity.this);
+                                container.setLayoutParams(params);
+                                container.addView(nodePlayerView);
+                                container.addView(loadingText);
+
+                                // 保存引用
+                                monitorInfo.setLoadingText(loadingText);
+                                monitorInfo.setNodePlayerView(nodePlayerView);
+                                monitorInfo.setMonitorVo(monitorVo);
+                                monitorInfo.setContainerView(container);
+                                monitorInfoList.add(monitorInfo);
 
-                                    @Override
-                                    public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
+                                // 立即添加到布局
+                                binding.gridLayout.addView(container);
 
-                                    }
+                                // 立即开始配置播放器
+                                final int index = i;
+                                final NodePlayer nodePlayer = new NodePlayer(MonitorActivity.this);
 
-                                    @Override
-                                    public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
-                                        return false;
-                                    }
+                                // 基础配置
+                                nodePlayer.setPlayerView(nodePlayerView);
+
+                                try {
+                                    nodePlayer.setRtspTransport("1");
+                                    nodePlayer.setVideoEnable(true);
+                                    nodePlayer.setAudioEnable(false);
+                                    nodePlayer.setBufferTime(100);
+                                    nodePlayer.setMaxBufferTime(300);
+                                } catch (Exception e) {
+                                    LogUtils.e("配置失败: " + e.getMessage());
+                                }
+
+                                // 硬件解码
+                                try {
+                                    Method setHWEnableMethod = nodePlayer.getClass()
+                                            .getMethod("setHWEnable", boolean.class);
+                                    setHWEnableMethod.invoke(nodePlayer, true);
+                                } catch (Exception e) {
+                                    // 忽略
+                                }
+
+                                // 地址优化
+                                String streamUrl = monitorVo.streamUrl;
+                                String optimizedUrl = streamUrl;
+
+                                if (streamUrl.contains("stream=0")) {
+                                    optimizedUrl = streamUrl.replace("stream=0", "stream=1");
+                                } else if (streamUrl.contains("main")) {
+                                    optimizedUrl = streamUrl.replace("main", "sub");
+                                }
+
+                                // 设置地址
+                                try {
+                                    nodePlayer.setInputUrl(optimizedUrl);
+                                } catch (Exception e) {
+                                    LogUtils.e("设置地址失败: " + e.getMessage());
+                                    nodePlayer.setInputUrl(streamUrl);
+                                }
 
+                                // 保存播放器
+                                monitorInfo.setNodePlayer(nodePlayer);
+
+                                // 立即启动播放器
+                                try {
+                                    nodePlayer.start();
+                                } catch (Exception e) {
+                                    LogUtils.e("启动失败: " + e.getMessage());
+                                }
+
+                                // 设置点击事件 - 修复版
+                                final MonitorInfo finalMonitorInfo = monitorInfo;
+                                container.setOnClickListener(new View.OnClickListener() {
                                     @Override
-                                    public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
+                                    public void onClick(View v) {
+                                        if (isExiting || isDialogShowing) {
+                                            return;
+                                        }
 
+                                        for (int j = 0; j < monitorInfoList.size(); j++) {
+                                            MonitorInfo info = monitorInfoList.get(j);
+                                            if (info.getContainerView() == v) {
+                                                showFullScreenDialog(info);
+                                                break;
+                                            }
+                                        }
                                     }
                                 });
 
-                                textureView.setOnClickListener(new FastClickDelegate(new Function1<View, Unit>() {
+                                // 状态监听
+                                final TextView finalLoadingText = loadingText;
+                                nodePlayer.setNodePlayerDelegate(new NodePlayerDelegate() {
                                     @Override
-                                    public Unit invoke(View view) {
-                                        for (int j = 0; j < monitorInfoList.size(); j++) {
-                                            MonitorInfo monitorInfo = monitorInfoList.get(j);
-                                            if (monitorInfo.getTextureView() == view) {
-                                                monitorDialog = new MonitorDialog(MonitorActivity.this, monitorInfo, new DialogInterface.OnDismissListener() {
+                                    public void onEventCallback(NodePlayer player, int event, String msg) {
+                                        {
+                                            if (event == 1000 || event == 1001 || event == 2000 || event == 2001) {
+                                                runOnUiThread(new Runnable() {
                                                     @Override
-                                                    public void onDismiss(DialogInterface dialog) {
-                                                        dialog.dismiss();
-                                                        monitorDialog = null;
+                                                    public void run() {
+                                                        if (finalLoadingText.getVisibility() == View.VISIBLE) {
+                                                            finalLoadingText.setVisibility(View.GONE);
+                                                            loadingCounter.incrementAndGet();
+                                                            if (loadingCounter.get() == totalPlayers) {
+                                                                LogUtils.d("所有播放器加载完成");
+                                                            }
+                                                        }
                                                     }
                                                 });
-                                                monitorDialog.show();
-                                                break;
                                             }
                                         }
-                                        return null;
-                                    }
-                                }));
-
-                                HatomPlayer hatomPlayer = new DefaultHatomPlayer();
-                                PlayConfig playConfig = new PlayConfig();
-                                playConfig.hardDecode = true;
-                                playConfig.privateData = true;
-                                hatomPlayer.setPlayConfig(playConfig);
-                                hatomPlayer.setPlayStatusCallback(new PlayCallback.PlayStatusCallback() {
-                                    @Override
-                                    public void onPlayerStatus(@NonNull PlayCallback.Status status, String s) {
-                                        LogUtils.d(status, s);
                                     }
                                 });
 
-                                monitorInfo.setTextureView(textureView);
-                                monitorInfo.setMonitorVo(monitorVo);
-                                monitorInfo.setHatomPlayer(hatomPlayer);
-
-                                monitorInfoList.add(monitorInfo);
+                                // 快速超时机制
+                                mainHandler.postDelayed(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        if (finalLoadingText != null && finalLoadingText.getVisibility() == View.VISIBLE) {
+                                            finalLoadingText.setText("正在连接");
+                                        }
+                                    }
+                                }, 500);
 
-                                binding.gridLayout.addView(textureView);
+                                mainHandler.postDelayed(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        if (finalLoadingText != null && finalLoadingText.getVisibility() == View.VISIBLE) {
+                                            finalLoadingText.setVisibility(View.GONE);
+                                            loadingCounter.incrementAndGet();
+                                        }
+                                    }
+                                }, 1500);
                             }
-
-                            dismissLoading();
                         }
                     });
                 } else {
@@ -207,34 +277,317 @@ public class MonitorActivity extends BaseCountDownActivity<ActivityMonitorBindin
             public void accept(Throwable throwable) throws Throwable {
                 dismissLoading();
                 LogUtils.e(Log.getStackTraceString(throwable));
+                showToast("加载监控失败");
             }
         }));
     }
 
-    private void play(HatomPlayer hatomPlayer, SurfaceTexture surfaceTexture, String url) {
-        ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
+    /**
+     * 显示全屏Dialog
+     */
+    private void showFullScreenDialog(final MonitorInfo monitorInfo) {
+        if (isExiting || isDialogShowing || monitorInfo == null) {
+            return;
+        }
+
+        isDialogShowing = true;
+
+        // 先暂停小窗口播放(避免切换时的闪烁)
+        NodePlayer player = monitorInfo.getNodePlayer();
+        if (player != null) {
+            try {
+                player.pause();
+            } catch (Exception e) {
+                LogUtils.e("暂停播放失败: " + e.getMessage());
+            }
+        }
+
+        monitorDialog = new MonitorDialog(MonitorActivity.this, monitorInfo, new DialogInterface.OnDismissListener() {
             @Override
-            public Object doInBackground() throws Throwable {
-                hatomPlayer.setSurfaceTexture(surfaceTexture);
-                hatomPlayer.setDataSource(url, null);
-                hatomPlayer.changeStream(Quality.SUB_STREAM_LOW);
-                hatomPlayer.start();
-                return null;
+            public void onDismiss(DialogInterface dialog) {
+                LogUtils.d("Dialog已关闭,恢复小窗口播放");
+
+                // 延迟恢复小窗口播放
+                mainHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        restoreSmallWindow(monitorInfo);
+                    }
+                }, 100);
+
+                isDialogShowing = false;
+                monitorDialog = null;
+            }
+        });
+
+        // 显示Dialog
+        try {
+            monitorDialog.show();
+        } catch (Exception e) {
+            LogUtils.e("显示Dialog失败: " + e.getMessage());
+            isDialogShowing = false;
+            monitorDialog = null;
+
+            // 失败时恢复小窗口
+            restoreSmallWindow(monitorInfo);
+        }
+    }
+
+    /**
+     * 恢复小窗口播放
+     */
+    private void restoreSmallWindow(MonitorInfo monitorInfo) {
+        if (monitorInfo == null || isExiting) {
+            return;
+        }
+
+        NodePlayer player = monitorInfo.getNodePlayer();
+        NodePlayerView originalView = monitorInfo.getNodePlayerView();
+
+        if (player != null && originalView != null) {
+            try {
+                // 确保播放器视图是原始小窗口视图
+                player.setPlayerView(originalView);
+
+                // 延迟后开始播放
+                mainHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (!isExiting) {
+                            try {
+                                player.start();
+                                LogUtils.d("小窗口播放已恢复");
+                            } catch (Exception e) {
+                                LogUtils.e("恢复播放失败: " + e.getMessage());
+                            }
+                        }
+                    }
+                }, 200);
+            } catch (Exception e) {
+                LogUtils.e("恢复小窗口视图失败: " + e.getMessage());
             }
+        }
+    }
+
+    /**
+     * 安全退出Activity
+     */
+    private void safeExitActivity() {
+        if (isExiting) {
+            return;
+        }
+        isExiting = true;
+
+        LogUtils.d("开始安全退出流程");
+        showLoading("正在退出...");
+
+        // 1. 先关闭Dialog(如果有)
+        if (isDialogShowing && monitorDialog != null && monitorDialog.isShowing()) {
+            LogUtils.d("正在关闭全屏Dialog");
+            monitorDialog.dismiss();
+            monitorDialog = null;
+            isDialogShowing = false;
+
+            // 等待Dialog完全关闭
+            mainHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    executeExitProcedure();
+                }
+            }, 300);
+        } else {
+            // 直接执行退出流程
+            executeExitProcedure();
+        }
+    }
+
+    /**
+     * 执行退出流程
+     */
+    private void executeExitProcedure() {
+        LogUtils.d("执行退出流程");
+
+        // 1. 停止所有播放器
+        stopAllPlayers();
 
+        // 2. 清理UI并释放资源
+        runOnUiThread(new Runnable() {
             @Override
-            public void onSuccess(Object result) {
+            public void run() {
+                try {
+                    // 清理网格布局
+                    binding.gridLayout.removeAllViews();
+
+                    // 释放播放器资源
+                    releaseAllPlayers();
+
+                    dismissLoading();
+
+                    // 延迟finish确保资源释放完成
+                    mainHandler.postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (!isFinishing()) {
+                                finish();
+                            }
+                        }
+                    }, 200);
 
+                } catch (Exception e) {
+                    LogUtils.e("退出流程异常: " + e.getMessage());
+                    // 即使异常也要finish
+                    dismissLoading();
+                    if (!isFinishing()) {
+                        finish();
+                    }
+                }
             }
         });
     }
 
+    /**
+     * 停止所有播放器
+     */
+    private void stopAllPlayers() {
+        if (monitorInfoList != null) {
+            for (MonitorInfo monitorInfo : monitorInfoList) {
+                NodePlayer nodePlayer = monitorInfo.getNodePlayer();
+                if (nodePlayer != null) {
+                    try {
+                        nodePlayer.stop();
+                        LogUtils.d("停止播放器");
+                    } catch (Exception e) {
+                        LogUtils.e("停止播放器失败: " + e.getMessage());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 释放所有播放器资源
+     */
+    private void releaseAllPlayers() {
+        LogUtils.d("开始释放播放器资源");
+        if (monitorInfoList != null) {
+            for (MonitorInfo monitorInfo : monitorInfoList) {
+                NodePlayer nodePlayer = monitorInfo.getNodePlayer();
+                if (nodePlayer != null) {
+                    try {
+                        nodePlayer.release();
+                        monitorInfo.setNodePlayer(null);
+                        LogUtils.d("释放播放器成功");
+                    } catch (Exception e) {
+                        LogUtils.e("释放播放器失败: " + e.getMessage());
+                    }
+                }
+
+                // 清理其他引用
+                monitorInfo.setNodePlayerView(null);
+                monitorInfo.setContainerView(null);
+                monitorInfo.setLoadingText(null);
+                monitorInfo.setMonitorVo(null);
+            }
+            monitorInfoList.clear();
+        }
+        LogUtils.d("资源释放完成");
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        LogUtils.d("onResume - isExiting:" + isExiting);
+
+        // 恢复播放(如果不在退出状态)
+        if (!isExiting && monitorInfoList != null) {
+            // 只恢复前几个播放器,避免同时启动
+            int count = Math.min(monitorInfoList.size(), 4);
+            for (int i = 0; i < count; i++) {
+                MonitorInfo monitorInfo = monitorInfoList.get(i);
+                NodePlayer nodePlayer = monitorInfo.getNodePlayer();
+                if (nodePlayer != null) {
+                    try {
+                        // 第一个立即恢复,其他的延迟一点
+                        if (i == 0) {
+                            nodePlayer.start();
+                        } else {
+                            final int index = i;
+                            mainHandler.postDelayed(new Runnable() {
+                                @Override
+                                public void run() {
+                                    if (!isExiting && index < monitorInfoList.size()) {
+                                        NodePlayer player = monitorInfoList.get(index).getNodePlayer();
+                                        if (player != null) {
+                                            try {
+                                                player.start();
+                                            } catch (Exception e) {
+                                                LogUtils.e("恢复播放失败: " + e.getMessage());
+                                            }
+                                        }
+                                    }
+                                }
+                            }, i * 100);
+                        }
+                    } catch (Exception e) {
+                        LogUtils.e("恢复播放失败: " + e.getMessage());
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        LogUtils.d("onPause - isExiting:" + isExiting);
+
+        // 暂停播放但不停止(如果不在退出状态且没有显示Dialog)
+        if (!isExiting && !isDialogShowing && monitorInfoList != null) {
+            for (int i = 0; i < monitorInfoList.size(); i++) {
+                MonitorInfo monitorInfo = monitorInfoList.get(i);
+                NodePlayer nodePlayer = monitorInfo.getNodePlayer();
+                if (null != nodePlayer) {
+                    try {
+                        nodePlayer.pause();
+                    } catch (Exception e) {
+                        LogUtils.e("暂停播放失败: " + e.getMessage());
+                    }
+                }
+            }
+        }
+    }
+
     @Override
     protected void onDestroy() {
+        LogUtils.d("onDestroy - 开始清理资源");
+        isExiting = true;
+        isDialogShowing = false;
+
+        // 清理Handler
+        if (mainHandler != null) {
+            mainHandler.removeCallbacksAndMessages(null);
+        }
+
+        // 关闭Dialog
+        if (monitorDialog != null && monitorDialog.isShowing()) {
+            try {
+                monitorDialog.dismiss();
+            } catch (Exception e) {
+                LogUtils.e("关闭Dialog失败: " + e.getMessage());
+            }
+            monitorDialog = null;
+        }
+
+        // 释放所有资源
+        releaseAllPlayers();
+
+        // 清理Fragment
         Fragment fragment = getSupportFragmentManager().findFragmentByTag("video_dialog");
         if (fragment instanceof VideoDialogFragment) {
             ((VideoDialogFragment) fragment).dismissAllowingStateLoss();
         }
+
         super.onDestroy();
+        LogUtils.d("onDestroy - 资源清理完成");
     }
 }

+ 369 - 68
app/src/main/java/xn/xxp/main/monitor/MonitorDialog.java

@@ -2,140 +2,441 @@ package xn.xxp.main.monitor;
 
 import android.app.Dialog;
 import android.content.Context;
-import android.graphics.SurfaceTexture;
 import android.os.Bundle;
-import android.view.TextureView;
 import android.view.View;
 import android.view.Window;
+import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.blankj.utilcode.util.LogUtils;
 import com.blankj.utilcode.util.ThreadUtils;
-import com.hikvision.hatomplayer.DefaultHatomPlayer;
-import com.hikvision.hatomplayer.HatomPlayer;
-import com.hikvision.hatomplayer.PlayCallback;
-import com.hikvision.hatomplayer.PlayConfig;
-import com.hikvision.hatomplayer.core.Quality;
+
+import cn.nodemedia.NodePlayer;
+import cn.nodemedia.NodePlayerDelegate;
+import cn.nodemedia.NodePlayerView;
 
 import core.util.FastClickDelegate;
 import kotlin.Unit;
 import kotlin.jvm.functions.Function1;
 import xn.xxp.R;
 
-
 public class MonitorDialog extends Dialog {
 
     private final MonitorInfo monitorInfo;
-    private HatomPlayer hatomPlayer;
+    private NodePlayer nodePlayer;
     private TextView textView;
+    private TextView loadingText;
+    private ProgressBar loadingProgress;
+    private NodePlayerView nodePlayerView;
+    private boolean isUsingOriginalPlayer = false;
 
     public MonitorDialog(@NonNull Context context, MonitorInfo monitorInfo, OnDismissListener dismissListener) {
         super(context, R.style.FullScreenDialog);
         this.monitorInfo = monitorInfo;
         setOnDismissListener(dismissListener);
+
+        // 设置对话框为全屏
+        if (getWindow() != null) {
+            getWindow().setFlags(
+                    android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                    android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN
+            );
+        }
     }
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.dialog_monitor);
+
+        // 设置窗口动画
         Window window = getWindow();
         if (window != null) {
             window.setWindowAnimations(R.style.DialogAnimation);
+            window.setLayout(
+                    android.view.WindowManager.LayoutParams.MATCH_PARENT,
+                    android.view.WindowManager.LayoutParams.MATCH_PARENT
+            );
         }
+
         initVideoPlayer();
     }
 
     private void initVideoPlayer() {
-        TextureView textureView = findViewById(R.id.textureView);
+        LogUtils.d("初始化全屏播放器 - 复用原始播放器");
+
+        // 获取返回按钮
         textView = findViewById(R.id.back);
-        textView.setOnClickListener(new FastClickDelegate(new Function1<View, Unit>() {
+        if (textView == null) {
+            LogUtils.e("找不到返回按钮");
+            return;
+        }
+
+        // 设置返回按钮点击事件
+        textView.setOnClickListener(new View.OnClickListener() {
             @Override
-            public Unit invoke(View view) {
+            public void onClick(View v) {
+                LogUtils.d("点击返回按钮");
                 textView.setText("正在退出...");
-                releasePlayer();
-                return null;
+                dismiss();
             }
-        }));
-        textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
+        });
+
+        // 获取布局中的TextureView
+        View oldTextureView = findViewById(R.id.textureView);
+        if (oldTextureView == null) {
+            LogUtils.e("找不到textureView");
+            return;
+        }
+
+        // 获取父布局
+        android.view.ViewGroup parent = (android.view.ViewGroup) oldTextureView.getParent();
+        if (parent == null) {
+            LogUtils.e("textureView没有父布局");
+            return;
+        }
+
+        // 创建NodePlayerView(用于全屏显示)
+        nodePlayerView = new NodePlayerView(getContext());
+        android.widget.RelativeLayout.LayoutParams params = new android.widget.RelativeLayout.LayoutParams(
+                android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+                android.view.ViewGroup.LayoutParams.MATCH_PARENT
+        );
+        nodePlayerView.setLayoutParams(params);
+        nodePlayerView.setKeepScreenOn(true);
+
+        // 替换原TextureView
+        int index = parent.indexOfChild(oldTextureView);
+        parent.removeView(oldTextureView);
+        parent.addView(nodePlayerView, index);
+
+        LogUtils.d("NodePlayerView已添加到布局");
+
+        // 获取原始播放器
+        nodePlayer = monitorInfo.getNodePlayer();
+        if (nodePlayer == null) {
+            LogUtils.e("原始播放器为空,创建新的");
+            createNewPlayer();
+        } else {
+            // === 关键优化:复用原始播放器 ===
+            reuseOriginalPlayer();
+        }
+    }
+
+    /**
+     * 复用原始播放器(0延迟)
+     */
+    private void reuseOriginalPlayer() {
+        LogUtils.d("复用原始播放器 - 立即显示");
+        isUsingOriginalPlayer = true;
+
+        try {
+            // 1. 立即设置新的播放器视图
+            nodePlayer.setPlayerView(nodePlayerView);
+            LogUtils.d("已切换播放器视图到全屏");
+
+            // 2. 如果播放器是暂停状态,恢复播放
+            nodePlayer.start();
+
+            // 3. 立即显示(不需要等待缓冲)
+            showImmediately();
+
+            // 4. 添加监听器(可选)
+            nodePlayer.setNodePlayerDelegate(new NodePlayerDelegate() {
+                @Override
+                public void onEventCallback(NodePlayer player, int event, String msg) {
+                    {
+                        if (event == 1000 || event == 1001 || event == 2000 || event == 2001) {
+                            LogUtils.d("全屏播放器开始播放");
+                            hideLoadingImmediately();
+                        } else if (event == 1003 || event == 2003) {
+                            LogUtils.e("全屏播放失败: " + msg);
+                        }
+                    }
+                }
+            });
+
+        } catch (Exception e) {
+            LogUtils.e("复用播放器失败: " + e.getMessage());
+            // 失败时创建新播放器
+            createNewPlayer();
+        }
+    }
+
+    /**
+     * 立即显示(隐藏loading)
+     */
+    private void showImmediately() {
+        // 不需要loading,因为视频已经在播放
+        if (loadingText != null) {
+            loadingText.setVisibility(View.GONE);
+        }
+        if (loadingProgress != null) {
+            loadingProgress.setVisibility(View.GONE);
+        }
+        LogUtils.d("立即显示视频画面");
+    }
+
+    /**
+     * 创建新的播放器(备用方案)
+     */
+    private void createNewPlayer() {
+        LogUtils.d("创建新的全屏播放器");
+        isUsingOriginalPlayer = false;
+
+        // 显示加载提示
+        setupLoadingView();
+
+        ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
             @Override
-            public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
-                ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
-                    @Override
-                    public Object doInBackground() throws Throwable {
-                        hatomPlayer = new DefaultHatomPlayer();
-                        PlayConfig playConfig = new PlayConfig();
-                        playConfig.hardDecode = true;
-                        playConfig.privateData = true;
-                        hatomPlayer.setPlayConfig(playConfig);
-                        hatomPlayer.setPlayStatusCallback(new PlayCallback.PlayStatusCallback() {
-                            @Override
-                            public void onPlayerStatus(@NonNull PlayCallback.Status status, String s) {
-                                LogUtils.d(status, s);
-                            }
-                        });
-                        hatomPlayer.setSurfaceTexture(surface);
-                        hatomPlayer.setDataSource(monitorInfo.getMonitorVo().streamUrl, null);
-                        hatomPlayer.changeStream(Quality.STREAM_SUPER_CLEAR);
-                        hatomPlayer.start();
+            public Object doInBackground() throws Throwable {
+                try {
+                    // 检查流地址
+                    if (monitorInfo == null || monitorInfo.getMonitorVo() == null) {
+                        LogUtils.e("监控信息为空");
+                        hideLoading();
                         return null;
                     }
 
-                    @Override
-                    public void onSuccess(Object result) {
+                    String streamUrl = monitorInfo.getMonitorVo().streamUrl;
+                    if (streamUrl == null || streamUrl.isEmpty()) {
+                        LogUtils.e("流地址为空");
+                        hideLoading();
+                        return null;
+                    }
+
+                    LogUtils.d("创建新播放器,流地址: " + streamUrl);
+
+                    // 创建新的NodePlayer
+                    nodePlayer = new NodePlayer(getContext());
+
+                    // 设置播放视图
+                    if (nodePlayerView != null) {
+                        nodePlayer.setPlayerView(nodePlayerView);
+                    }
 
+                    // 快速配置
+                    nodePlayer.setRtspTransport("1");
+                    nodePlayer.setVideoEnable(true);
+                    nodePlayer.setAudioEnable(false);
+
+                    // 使用稍大的缓冲(200ms),保证快速连接
+                    try {
+                        nodePlayer.setBufferTime(200);
+                        nodePlayer.setMaxBufferTime(600);
+                    } catch (Exception e) {
+                        // 忽略
+                    }
+
+                    // 硬件解码
+                    try {
+                        java.lang.reflect.Method setHWEnableMethod = nodePlayer.getClass()
+                                .getMethod("setHWEnable", boolean.class);
+                        setHWEnableMethod.invoke(nodePlayer, true);
+                    } catch (Exception e) {
+                        // 忽略
                     }
-                });
+
+                    // 使用子码流(如果可用)
+                    String optimizedUrl = streamUrl;
+                    if (streamUrl.contains("stream=0")) {
+                        optimizedUrl = streamUrl.replace("stream=0", "stream=1");
+                    } else if (streamUrl.contains("main")) {
+                        optimizedUrl = streamUrl.replace("main", "sub");
+                    }
+
+                    // 设置地址并启动
+                    nodePlayer.setInputUrl(optimizedUrl);
+                    nodePlayer.start();
+
+                    // 添加监听器
+                    nodePlayer.setNodePlayerDelegate(new NodePlayerDelegate() {
+                        @Override
+                        public void onEventCallback(NodePlayer player, int event, String msg) {
+                            {
+                                if (event == 1000 || event == 1001 || event == 2000 || event == 2001) {
+                                    LogUtils.d("新播放器开始播放");
+                                    // 1秒后隐藏loading(给缓冲时间)
+                                    ThreadUtils.runOnUiThreadDelayed(new Runnable() {
+                                        @Override
+                                        public void run() {
+                                            hideLoading();
+                                        }
+                                    }, 1000);
+                                }
+                            }
+                        }
+                    });
+
+                    // 3秒超时强制隐藏loading
+                    ThreadUtils.runOnUiThreadDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            hideLoading();
+                        }
+                    }, 3000);
+
+                } catch (Exception e) {
+                    LogUtils.e("创建播放器失败: " + e.getMessage());
+                    hideLoadingWithError("播放失败");
+                }
+                return null;
             }
 
             @Override
-            public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
+            public void onSuccess(Object result) {
+                LogUtils.d("创建播放器成功");
+            }
+        });
+    }
+
+    private void setupLoadingView() {
+        // 创建加载视图(与之前类似但简化)
+        if (loadingText == null) {
+            loadingText = new TextView(getContext());
+            android.widget.RelativeLayout.LayoutParams textParams = new android.widget.RelativeLayout.LayoutParams(
+                    android.widget.RelativeLayout.LayoutParams.WRAP_CONTENT,
+                    android.widget.RelativeLayout.LayoutParams.WRAP_CONTENT
+            );
+            textParams.addRule(android.widget.RelativeLayout.CENTER_IN_PARENT);
+            loadingText.setLayoutParams(textParams);
+            loadingText.setText("加载中...");
+            loadingText.setTextColor(0xFFFFFFFF);
+            loadingText.setTextSize(14);
+            loadingText.setVisibility(View.VISIBLE);
 
+            if (nodePlayerView != null && nodePlayerView.getParent() != null) {
+                ((android.view.ViewGroup) nodePlayerView.getParent()).addView(loadingText);
             }
+        }
+    }
 
+    private void hideLoadingImmediately() {
+        ThreadUtils.runOnUiThread(new Runnable() {
             @Override
-            public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
-                return false;
+            public void run() {
+                if (loadingText != null) {
+                    loadingText.setVisibility(View.GONE);
+                }
+                if (loadingProgress != null) {
+                    loadingProgress.setVisibility(View.GONE);
+                }
             }
+        });
+    }
 
-            @Override
-            public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
+    private void hideLoading() {
+        hideLoadingImmediately();
+    }
 
+    private void hideLoadingWithError(String error) {
+        ThreadUtils.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (loadingText != null) {
+                    loadingText.setText(error);
+                    // 3秒后隐藏
+                    ThreadUtils.runOnUiThreadDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (loadingText != null) {
+                                loadingText.setVisibility(View.GONE);
+                            }
+                        }
+                    }, 3000);
+                }
             }
         });
     }
 
     @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        releasePlayer();
-    }
+    public void dismiss() {
+        LogUtils.d("Dialog dismiss - 恢复小窗口播放");
 
-    /**
-     * 释放播放器资源
-     */
-    private void releasePlayer() {
-        if (hatomPlayer != null) {
-            dismiss();
-            ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
-                @Override
-                public Object doInBackground() throws Throwable {
-                    hatomPlayer.stop(); // 释放播放器
-                    return null;
-                }
+        // === 关键修复:先恢复原始视图,再清理Dialog ===
+        if (isUsingOriginalPlayer && nodePlayer != null && monitorInfo != null) {
+            try {
+                // 1. 先暂停播放(避免切换时的闪烁)
+                nodePlayer.pause();
 
-                @Override
-                public void onSuccess(Object result) {
-                    dismiss();
+                // 2. 切换到原始视图
+                NodePlayerView originalView = monitorInfo.getNodePlayerView();
+                if (originalView != null && originalView.getParent() != null) {
+                    nodePlayer.setPlayerView(originalView);
+                    LogUtils.d("已恢复小窗口视图");
+                } else {
+                    LogUtils.e("原始视图无效或已被移除");
                 }
-            });
-            textView.setOnClickListener(null);
-            hatomPlayer = null; // 置空防止内存泄漏
-        } else {
-            dismiss();
+
+                // 3. 重新开始播放(在原始视图中)
+                ThreadUtils.runOnUiThreadDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            nodePlayer.start();
+                        } catch (Exception e) {
+                            LogUtils.e("恢复播放失败: " + e.getMessage());
+                        }
+                    }
+                }, 100);
+
+            } catch (Exception e) {
+                LogUtils.e("恢复小窗口失败: " + e.getMessage());
+            }
+        }
+
+        // === 关键:不要释放播放器!只是清理Dialog的资源 ===
+        if (!isUsingOriginalPlayer && nodePlayer != null) {
+            // 只释放新创建的播放器
+            try {
+                nodePlayer.stop();
+                nodePlayer.release();
+            } catch (Exception e) {
+                LogUtils.e("释放新播放器失败: " + e.getMessage());
+            }
+        } else if (isUsingOriginalPlayer) {
+            // === 关键:不要释放原始播放器!置空引用即可 ===
+            nodePlayer = null;
+        }
+
+        // 清理Dialog的视图
+        if (nodePlayerView != null && nodePlayerView.getParent() != null) {
+            ((android.view.ViewGroup) nodePlayerView.getParent()).removeView(nodePlayerView);
+        }
+
+        // 清理加载视图
+        if (loadingText != null && loadingText.getParent() != null) {
+            ((android.view.ViewGroup) loadingText.getParent()).removeView(loadingText);
+        }
+        if (loadingProgress != null && loadingProgress.getParent() != null) {
+            ((android.view.ViewGroup) loadingProgress.getParent()).removeView(loadingProgress);
+        }
+
+        // 清理引用
+        nodePlayerView = null;
+        loadingText = null;
+        loadingProgress = null;
+
+        // 最后调用父类的dismiss
+        try {
+            super.dismiss();
+        } catch (Exception e) {
+            LogUtils.e("super.dismiss()失败: " + e.getMessage());
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        // 只暂停新创建的播放器,复用播放器不处理
+        if (!isUsingOriginalPlayer && nodePlayer != null) {
+            try {
+                nodePlayer.pause();
+            } catch (Exception e) {
+                // 忽略
+            }
         }
     }
 }

+ 51 - 14
app/src/main/java/xn/xxp/main/monitor/MonitorInfo.java

@@ -1,16 +1,21 @@
 package xn.xxp.main.monitor;
 
-import android.view.TextureView;
-
-import com.hikvision.hatomplayer.HatomPlayer;
+import android.view.View;
+import android.widget.TextView;
 
+import cn.nodemedia.NodePlayer;
+import cn.nodemedia.NodePlayerView;
 import http.vo.response.MonitorVo;
 
 public class MonitorInfo {
-    private TextureView textureView;
-    private HatomPlayer hatomPlayer;
+    private NodePlayerView nodePlayerView;
+    private NodePlayer nodePlayer;
     private MonitorVo monitorVo;
     private boolean isFull;
+    private View containerView;
+    private TextView loadingText; // 添加加载文本引用
+    private boolean isPlaying = false; // 添加播放状态
+    private TextView loadingTextView; // 加载文本视图
 
     public boolean isFull() {
         return isFull;
@@ -20,20 +25,20 @@ public class MonitorInfo {
         isFull = full;
     }
 
-    public TextureView getTextureView() {
-        return textureView;
+    public NodePlayerView getNodePlayerView() {
+        return nodePlayerView;
     }
 
-    public void setTextureView(TextureView textureView) {
-        this.textureView = textureView;
+    public void setNodePlayerView(NodePlayerView nodePlayerView) {
+        this.nodePlayerView = nodePlayerView;
     }
 
-    public HatomPlayer getHatomPlayer() {
-        return hatomPlayer;
+    public NodePlayer getNodePlayer() {
+        return nodePlayer;
     }
 
-    public void setHatomPlayer(HatomPlayer hatomPlayer) {
-        this.hatomPlayer = hatomPlayer;
+    public void setNodePlayer(NodePlayer nodePlayer) {
+        this.nodePlayer = nodePlayer;
     }
 
     public MonitorVo getMonitorVo() {
@@ -43,4 +48,36 @@ public class MonitorInfo {
     public void setMonitorVo(MonitorVo monitorVo) {
         this.monitorVo = monitorVo;
     }
-}
+
+    public View getContainerView() {
+        return containerView;
+    }
+
+    public void setContainerView(View containerView) {
+        this.containerView = containerView;
+    }
+
+    public TextView getLoadingText() {
+        return loadingText;
+    }
+
+    public void setLoadingText(TextView loadingText) {
+        this.loadingText = loadingText;
+    }
+
+    public boolean isPlaying() {
+        return isPlaying;
+    }
+
+    public void setPlaying(boolean playing) {
+        isPlaying = playing;
+    }
+
+    public TextView getLoadingTextView() {
+        return loadingTextView;
+    }
+
+    public void setLoadingTextView(TextView loadingTextView) {
+        this.loadingTextView = loadingTextView;
+    }
+}

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

@@ -24,7 +24,7 @@
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent"
-            tools:src="@mipmap/icon_logo" />
+            tools:src="@mipmap/icon_logo_gzdx" />
 
         <TextView
             android:id="@+id/subName"

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

@@ -28,7 +28,7 @@
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toTopOf="parent"
-                tools:src="@mipmap/icon_logo" />
+                tools:src="@mipmap/icon_logo_gzdx" />
 
             <TextView
                 android:id="@+id/subName"

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

@@ -28,7 +28,7 @@
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toTopOf="parent"
-                tools:src="@mipmap/icon_logo" />
+                tools:src="@mipmap/icon_logo_gzdx" />
 
             <TextView
                 android:id="@+id/subName"

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

@@ -28,7 +28,7 @@
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toTopOf="parent"
-                tools:src="@mipmap/icon_logo" />
+                tools:src="@mipmap/icon_logo_gzdx" />
 
             <TextView
                 android:id="@+id/subName"

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

@@ -203,7 +203,7 @@
                         android:layout_marginStart="18px"
                         android:layout_weight="1"
                         android:gravity="center"
-                        android:text="安全联络人"
+                        android:text="安全"
                         android:textColor="#333333"
                         android:textSize="24px" />
 

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

@@ -17,7 +17,7 @@
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:src="@mipmap/icon_logo" />
+        tools:src="@mipmap/icon_logo_gzdx" />
 
     <TextView
         android:id="@+id/nowDate"

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

@@ -17,7 +17,7 @@
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:src="@mipmap/icon_logo" />
+        tools:src="@mipmap/icon_logo_gzdx" />
 
     <TextView
         android:id="@+id/subName"

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


+ 3 - 0
settings.gradle

@@ -13,6 +13,9 @@ pluginManagement {
         maven {
             url "https://maven.aliyun.com/repository/public"
         }
+        maven { url 'https://exoplayer.dev/repo' }
+        // 2. ZLMediaKit 示例里用到的 rtsp-extension 快照
+        maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
     }
 }
 dependencyResolutionManagement {