2.16
|
|
@ -11,7 +11,7 @@ android {
|
|||
minSdk 21
|
||||
targetSdk 34
|
||||
versionCode 1
|
||||
versionName "2.2.4-N"
|
||||
versionName "2.2.16-N"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@
|
|||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<!-- 查询网络状态(必要) -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<!-- 可选:如需获取 WiFi 详细信息(如 SSID),添加此权限 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
|
|
|
|||
BIN
XixunPlayer/app/src/main/assets/images/CELLULAR.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
XixunPlayer/app/src/main/assets/images/ETHERNET.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
XixunPlayer/app/src/main/assets/images/ID.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
XixunPlayer/app/src/main/assets/images/IP.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
XixunPlayer/app/src/main/assets/images/NONE.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
XixunPlayer/app/src/main/assets/images/WIFI.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
XixunPlayer/app/src/main/assets/images/app1.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
XixunPlayer/app/src/main/assets/images/app2.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
XixunPlayer/app/src/main/assets/images/app3.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
XixunPlayer/app/src/main/assets/images/arrow.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
XixunPlayer/app/src/main/assets/images/border5.mp4
Normal file
BIN
XixunPlayer/app/src/main/assets/images/code.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
XixunPlayer/app/src/main/assets/images/usb1.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
XixunPlayer/app/src/main/assets/images/usb2.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
XixunPlayer/app/src/main/assets/images/usb3.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
XixunPlayer/app/src/main/assets/images/web1.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
XixunPlayer/app/src/main/assets/images/web2.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
XixunPlayer/app/src/main/assets/images/web3.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
893
XixunPlayer/app/src/main/assets/local_page.html
Normal file
|
|
@ -0,0 +1,893 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||
<title>Android传参设置宽高</title>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="loading" id="loadingTip">正在接收Android宽高...</div>
|
||||
|
||||
<div id="redBorderContainer" style="flex-wrap: wrap;justify-content: space-between;align-items: center;">
|
||||
<!-- 顶部信息栏:统一容器+横向自适应布局 -->
|
||||
<div style="width:100%; margin-bottom: 1rem; position: relative; z-index: 1;padding: 2rem;">
|
||||
<!-- 顶部信息栏视频背景 -->
|
||||
<video class="info-bar-bg-video" autoplay muted loop playsinline
|
||||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: fill; z-index: -1; opacity:1;transform: scale(1.1);">
|
||||
<source src="images/border5.mp4" type="video/mp4">
|
||||
</video>
|
||||
<!-- 信息栏内容(z-index高于视频背景) -->
|
||||
<div
|
||||
style="width:80%;margin-left: 10%; display: flex; justify-content: space-between; align-items: center; padding: 1rem 0rem;position: relative; z-index: 1;">
|
||||
<div style="display: flex; align-items: center;justify-content: center; gap: 0.5rem; flex: 1;">
|
||||
<div
|
||||
style="width: 1.75rem; height: 1.75rem; display: flex; align-items: center; justify-content: center; ">
|
||||
<img src="images/ID.png" style="width: 2.5rem; height:2.5rem; object-fit: contain;">
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column;align-items:flex-start;margin-left: 0.5rem;">
|
||||
<div class="i18n-text" data-i18n="deviceId"
|
||||
style="font-size: 1.1rem; color: #f0f8fb; opacity: 0.9; line-height: 1;">设备ID</div>
|
||||
<div id="cardId"
|
||||
style="font-size: 1.4rem; font-weight: 600; color: #ffffff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
3568S-2C5-00641
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center;justify-content: center; gap: 0.5rem; flex: 1;">
|
||||
<div
|
||||
style="width: 1.75rem; height: 1.75rem; display: flex; align-items: center; justify-content: center;">
|
||||
<img id="networkIcon" src="images/NONE.png"
|
||||
style="width: 2.5rem; height:2.5rem; object-fit: contain;">
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column;align-items:flex-start;margin-left: 0.5rem;">
|
||||
<div class="i18n-text" data-i18n="networkStatus"
|
||||
style="font-size: 1.1rem; color: #f0f8fb; opacity: 0.9; line-height: 1;">网络状态</div>
|
||||
<div id="networkText"
|
||||
style="font-size: 1.4rem; font-weight: 600; color: #ffffff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
未知网络
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div style="display: flex; align-items: center;justify-content: center; gap: 0.5rem; flex: 1;">
|
||||
<div
|
||||
style="width: 1.75rem; height: 1.75rem; display: flex; align-items: center; justify-content: center;">
|
||||
<img src="images/IP.png" style="width: 2.5rem; height:2.5rem; object-fit: contain;">
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column;align-items:flex-start;margin-left: 0.5rem;">
|
||||
<div class="i18n-text" data-i18n="ipAddress"
|
||||
style="font-size: 1.1rem; color: #f0f8fb; opacity: 0.9; line-height: 1;">IP地址</div>
|
||||
<div id="networkIP"
|
||||
style="font-size: 1.4rem; font-weight: 600; color: #ffffff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
0.0.0.0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oneDivContainer">
|
||||
<div class="threeDivContainer">
|
||||
<!-- 👇 云平台发布卡片:添加独立视频背景 -->
|
||||
<div class="pageDiv">
|
||||
<!-- 卡片内视频背景(独立元素) -->
|
||||
<video class="card-bg-video" autoplay muted loop playsinline
|
||||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: fill; z-index: 0;opacity:1;transform:scale(1.1);">
|
||||
<source src="images/border5.mp4" type="video/mp4">
|
||||
</video>
|
||||
<!-- 卡片内容(z-index高于视频) -->
|
||||
<div style="position: relative; z-index: 1; width: 100%; height: 100%;">
|
||||
<div class="fontBold2 i18n-text" data-i18n="cloudPublish">云平台发布</div>
|
||||
<div class="sPage">
|
||||
<div>
|
||||
<div class="fontBold Hcenter">
|
||||
<span class="step-icon">📌</span>
|
||||
<span class="i18n-text" data-i18n="operationSteps">操作步骤</span>
|
||||
</div>
|
||||
<div class="Wcenter" style="margin-left: 0.5rem;">
|
||||
<div class="divText i18n-text" data-i18n="cloudStep1">1.登录到云平台</div>
|
||||
<div class="divText i18n-text" data-i18n="cloudStep2">2.创建并编辑节目</div>
|
||||
<div class="divText i18n-text" data-i18n="cloudStep3">3.发送节目到设备</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="display: flex;align-items: center;justify-content: space-around;width: 100%;">
|
||||
<img src="images/web1.png" class="imgSize1" style="object-fit: contain;">
|
||||
<img src="images/arrow.png" class="imgSize2"
|
||||
style="object-fit: contain;filter: brightness(0) invert(40%) sepia(90%) saturate(300%) hue-rotate(150deg) brightness(90%) contrast(90%);">
|
||||
<img src="images/web2.png" class="imgSize1" style="object-fit: contain;">
|
||||
<img src="images/arrow.png" class="imgSize2"
|
||||
style="object-fit: contain;filter: brightness(0) invert(40%) sepia(90%) saturate(300%) hue-rotate(150deg) brightness(90%) contrast(90%);">
|
||||
<img src="images/web3.png" class="imgSize1" style="object-fit: contain;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 100%;min-height: 25%;">
|
||||
<div class="fontBold Hcenter">
|
||||
<span class="step-icon">📌</span>
|
||||
<span class="i18n-text" data-i18n="notes">注意事项</span>
|
||||
</div>
|
||||
<div class="divText2 i18n-text" data-i18n="cloudNote">
|
||||
设备需切换至联网模式并关联云平台账号,云服务器地址:https://www.ledokcloud.com
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 👇 手机APP发布卡片:添加独立视频背景 -->
|
||||
<div class="pageDiv">
|
||||
<!-- 卡片内视频背景(独立元素) -->
|
||||
<video class="card-bg-video" autoplay muted loop playsinline
|
||||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: fill; z-index: 0;opacity:1;transform:scale(1.1);">
|
||||
<source src="images/border5.mp4" type="video/mp4">
|
||||
</video>
|
||||
<!-- 卡片内容(z-index高于视频) -->
|
||||
<div style="position: relative; z-index: 1; width: 100%; height: 100%;">
|
||||
<div class="fontBold2 i18n-text" data-i18n="appPublish">手机APP发布</div>
|
||||
<div class="sPage">
|
||||
<div>
|
||||
<div class="fontBold Hcenter">
|
||||
<span class="step-icon">📌</span>
|
||||
<span class="i18n-text" data-i18n="operationSteps">操作步骤</span>
|
||||
</div>
|
||||
<div class="Wcenter" style="margin-left: 0.5rem;">
|
||||
<div class="divText i18n-text" data-i18n="appStep1">1.打开LEDOK Lite APP</div>
|
||||
<div class="divText i18n-text" data-i18n="appStep2">2.创建并编辑节目</div>
|
||||
<div class="divText i18n-text" data-i18n="appStep3">3.发送节目到设备</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="display: flex;align-items: center;justify-content: space-around;width: 100%;">
|
||||
<img src="images/app1.png" class="imgSize1" style="object-fit: contain;">
|
||||
<img src="images/arrow.png" class="imgSize2"
|
||||
style="object-fit: contain;filter: brightness(0) invert(40%) sepia(90%) saturate(300%) hue-rotate(150deg) brightness(90%) contrast(90%);">
|
||||
<img src="images/app2.png" class="imgSize1" style="object-fit: contain;">
|
||||
<img src="images/arrow.png" class="imgSize2"
|
||||
style="object-fit: contain;filter: brightness(0) invert(40%) sepia(90%) saturate(300%) hue-rotate(150deg) brightness(90%) contrast(90%);">
|
||||
<img src="images/app3.png" class="imgSize1" style="object-fit: contain;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex;align-items: flex-start;justify-content: space-between;width: 100%;min-height: 25%;">
|
||||
<div>
|
||||
<div class="fontBold Hcenter">
|
||||
<span class="step-icon">📌</span>
|
||||
<span class="i18n-text" data-i18n="notes">注意事项</span>
|
||||
</div>
|
||||
<div class="divText2 i18n-text" data-i18n="appNote">
|
||||
设备需切换至热点模式,手机需连接该设备热点
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 1rem;"></div>
|
||||
<div>
|
||||
<img src="images/code.png" class="imgSize3"
|
||||
style="object-fit: contain;">
|
||||
<!-- <div class="qrcode-text i18n-text" data-i18n="scanDownload">扫码下载APP</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 👇 U盘发布卡片:添加独立视频背景 -->
|
||||
<div class="pageDiv">
|
||||
<!-- 卡片内视频背景(独立元素) -->
|
||||
<video class="card-bg-video" autoplay muted loop playsinline
|
||||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: fill; z-index: 0;opacity:1;transform:scale(1.1);">
|
||||
<source src="images/border5.mp4" type="video/mp4">
|
||||
</video>
|
||||
<!-- 卡片内容(z-index高于视频) -->
|
||||
<div style="position: relative; z-index: 1; width: 100%; height: 100%;">
|
||||
<div class="fontBold2 i18n-text" data-i18n="usbPublish">U盘发布</div>
|
||||
<div class="sPage">
|
||||
<div>
|
||||
<div class="fontBold Hcenter">
|
||||
<span class="step-icon">📌</span>
|
||||
<span class="i18n-text" data-i18n="operationSteps">操作步骤</span>
|
||||
</div>
|
||||
<div class="Wcenter" style="margin-left: 0.5rem;">
|
||||
<div class="divText i18n-text" data-i18n="usbStep1">1.节目文件存入U盘</div>
|
||||
<div class="divText i18n-text" data-i18n="usbStep2">2.U盘接入设备</div>
|
||||
<div class="divText i18n-text" data-i18n="usbStep3">3.系统自动播放</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="display: flex;align-items: center;justify-content: space-around;width: 100%;">
|
||||
<img src="images/usb1.png" class="imgSize1" style="object-fit: contain;">
|
||||
<img src="images/arrow.png" class="imgSize2"
|
||||
style="object-fit: contain;filter: brightness(0) invert(40%) sepia(90%) saturate(300%) hue-rotate(150deg) brightness(90%) contrast(90%);">
|
||||
<img src="images/usb2.png" class="imgSize1" style="object-fit: contain;">
|
||||
<img src="images/arrow.png" class="imgSize2"
|
||||
style="object-fit: contain;filter: brightness(0) invert(40%) sepia(90%) saturate(300%) hue-rotate(150deg) brightness(90%) contrast(90%);">
|
||||
<img src="images/usb3.png" class="imgSize1" style="object-fit: contain;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 100%;min-height: 25%;">
|
||||
<div class="fontBold Hcenter">
|
||||
<span class="step-icon">📌</span>
|
||||
<span class="i18n-text" data-i18n="notes">注意事项</span>
|
||||
</div>
|
||||
<div class="divText2 i18n-text" data-i18n="usbNote">
|
||||
无设备模式限制,U盘即插即播
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ===================== 1. 定义中英文语言包 =====================
|
||||
const i18n = {
|
||||
// 中文
|
||||
zh: {
|
||||
deviceId: "设备ID",
|
||||
networkStatus: "网络状态",
|
||||
ipAddress: "IP地址",
|
||||
noProgramTip: "当前无节目播放,可选择以下方式发布节目",
|
||||
cloudPublish: "云平台发布",
|
||||
appPublish: "手机APP发布",
|
||||
usbPublish: "U盘发布",
|
||||
operationSteps: "操作步骤",
|
||||
notes: "注意事项",
|
||||
cloudStep1: "1.登录到云平台",
|
||||
cloudStep2: "2.创建并编辑节目",
|
||||
cloudStep3: "3.发送节目到设备",
|
||||
cloudNote: "设备需切换至联网模式并关联云平台账号,云服务器地址:https://www.ledokcloud.com",
|
||||
appStep1: "1.打开LEDOK Lite APP",
|
||||
appStep2: "2.创建并编辑节目",
|
||||
appStep3: "3.发送节目到设备",
|
||||
appNote: "设备需切换至热点模式,手机需连接该设备热点",
|
||||
scanDownload: "扫码下载APP",
|
||||
usbStep1: "1.节目文件存入U盘",
|
||||
usbStep2: "2.U盘接入设备",
|
||||
usbStep3: "3.系统自动播放",
|
||||
usbNote: "无设备模式限制,U盘即插即播",
|
||||
loading: "正在接收Android宽高...",
|
||||
loadFail: "接收宽高失败",
|
||||
unknownNetwork: "未知网络"
|
||||
},
|
||||
// 英文
|
||||
en: {
|
||||
deviceId: "Device ID",
|
||||
networkStatus: "Network Status",
|
||||
ipAddress: "IP Address",
|
||||
noProgramTip: "No program is playing. Choose the following ways to publish programs",
|
||||
cloudPublish: "Cloud Platform Publish",
|
||||
appPublish: "Mobile APP Publish",
|
||||
usbPublish: "USB Publish",
|
||||
operationSteps: "Operation Steps",
|
||||
notes: "Notes",
|
||||
cloudStep1: "1.Login to the cloud platform",
|
||||
cloudStep2: "2.Create and edit programs",
|
||||
cloudStep3: "3.Send programs to device",
|
||||
cloudNote: "The device must be switched to network mode and associated with a cloud platform account. Cloud server address: https://www.ledokcloud.com",
|
||||
appStep1: "1.Open LEDOK Lite APP",
|
||||
appStep2: "2.Create and edit programs",
|
||||
appStep3: "3.Send programs to device",
|
||||
appNote: "The device must be switched to hotspot mode, and the mobile phone must connect to the device's hotspot",
|
||||
scanDownload: "Scan to download APP",
|
||||
usbStep1: "1.Save program files to USB",
|
||||
usbStep2: "2.Connect USB to device",
|
||||
usbStep3: "3.System plays automatically",
|
||||
usbNote: "No device mode restrictions, plug and play with USB",
|
||||
loading: "Receiving Android width and height...",
|
||||
loadFail: "Failed to receive width and height",
|
||||
unknownNetwork: "Unknown Network"
|
||||
}
|
||||
};
|
||||
|
||||
// ===================== 2. 全局变量 =====================
|
||||
let cachedData = {
|
||||
// 缓存上一次的数据,用于对比变化
|
||||
width: "",
|
||||
height: "",
|
||||
deviceId: "",
|
||||
ip: "",
|
||||
networkType: "",
|
||||
networkValue: ""
|
||||
};
|
||||
let refreshInterval = null; // 定时任务ID,用于后续可取消
|
||||
|
||||
// ===================== 3. 语言工具函数 =====================
|
||||
// 获取当前语言(优先navigator.language,默认中文)
|
||||
function getCurrentLang() {
|
||||
const lang = navigator.language.toLowerCase();
|
||||
console.log("浏览器语言检测:", lang);
|
||||
// 只支持zh和en,其他默认中文
|
||||
return lang.includes("en") ? "en" : "zh";
|
||||
}
|
||||
|
||||
// 切换语言(核心函数)
|
||||
function changeLanguage(lang) {
|
||||
// 1. 验证语言类型
|
||||
if (!["zh", "en"].includes(lang)) lang = "zh";
|
||||
|
||||
// 2. 更新所有带data-i18n属性的文本
|
||||
document.querySelectorAll(".i18n-text").forEach(el => {
|
||||
const key = el.getAttribute("data-i18n");
|
||||
if (i18n[lang][key]) {
|
||||
el.textContent = i18n[lang][key];
|
||||
}
|
||||
});
|
||||
|
||||
// 3. 更新其他动态文本(如加载提示、网络状态默认文本)
|
||||
document.getElementById("networkText").textContent = i18n[lang].unknownNetwork;
|
||||
|
||||
// 4. 更新HTML文档语言属性(语义化)
|
||||
document.documentElement.lang = lang;
|
||||
|
||||
console.log("语言切换完成:", lang);
|
||||
}
|
||||
|
||||
// ===================== 4. REM自适应计算 =====================
|
||||
function calcRemBase(container) {
|
||||
let baseWidth = 1920;
|
||||
let baseHeight = 1080;
|
||||
|
||||
const containerWidth = parseInt(container.style.width);
|
||||
const containerHeight = parseInt(container.style.height);
|
||||
|
||||
if (containerHeight > containerWidth) {
|
||||
baseWidth = 1080;
|
||||
baseHeight = 1920;
|
||||
}
|
||||
|
||||
const baseRem = 16;
|
||||
const scaleX = containerWidth / baseWidth;
|
||||
const scaleY = containerHeight / baseHeight;
|
||||
const scale = Math.min(scaleX, scaleY);
|
||||
const newRem = baseRem * scale;
|
||||
|
||||
document.documentElement.style.fontSize = newRem + "px";
|
||||
console.log(`rem基准更新:${newRem}px(缩放比例:${scale.toFixed(2)},基准尺寸:${baseWidth}x${baseHeight}`);
|
||||
}
|
||||
|
||||
// ===================== 5. 横竖屏判断 =====================
|
||||
function setOrientationClass(container) {
|
||||
const containerWidth = parseInt(container.style.width);
|
||||
const containerHeight = parseInt(container.style.height);
|
||||
|
||||
container.classList.remove("portrait", "landscape");
|
||||
if (containerHeight > containerWidth) {
|
||||
container.classList.add("portrait");
|
||||
console.log("竖屏模式:", containerWidth + "x" + containerHeight);
|
||||
} else {
|
||||
container.classList.add("landscape");
|
||||
console.log("横屏模式:", containerWidth + "x" + containerHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 6. 核心:数据获取与更新(抽取独立函数) =====================
|
||||
function fetchAndUpdateData() {
|
||||
const currentLang = document.documentElement.lang || "zh";
|
||||
const loadingTip = document.getElementById("loadingTip");
|
||||
const container = document.getElementById("redBorderContainer");
|
||||
const networkIcon = document.getElementById("networkIcon");
|
||||
const networkText = document.getElementById("networkText");
|
||||
const networkIP = document.getElementById("networkIP");
|
||||
const cardId = document.getElementById("cardId");
|
||||
const cardVideos = document.querySelectorAll(".card-bg-video"); // 所有卡片视频
|
||||
const infoBarVideo = document.querySelector(".info-bar-bg-video"); // 顶部信息栏视频
|
||||
|
||||
try {
|
||||
// ---------------------- 1. 获取并更新宽高 ----------------------
|
||||
const sizeJson = AndroidBridge.getSizeFromAndroid();
|
||||
const size = JSON.parse(sizeJson);
|
||||
const newWidth = size.width;
|
||||
const newHeight = size.height;
|
||||
|
||||
// 对比缓存:宽高变化时才更新(避免重复计算rem)
|
||||
if (newWidth !== cachedData.width || newHeight !== cachedData.height) {
|
||||
console.log("宽高变化,更新容器尺寸:", newWidth + "x" + newHeight);
|
||||
// 更新容器宽高
|
||||
container.style.width = newWidth;
|
||||
container.style.height = newHeight;
|
||||
// 更新横竖屏类名
|
||||
setOrientationClass(container);
|
||||
// 重新计算rem
|
||||
calcRemBase(container);
|
||||
// 更新缓存
|
||||
cachedData.width = newWidth;
|
||||
cachedData.height = newHeight;
|
||||
// 首次加载时显示容器、隐藏加载提示
|
||||
if (loadingTip.style.display !== "none") {
|
||||
loadingTip.style.display = "none";
|
||||
container.style.display = "flex";
|
||||
|
||||
// 顶部信息栏视频播放
|
||||
infoBarVideo.play().catch(err => {
|
||||
console.log("顶部信息栏视频自动播放失败:", err);
|
||||
container.addEventListener('click', () => infoBarVideo.play(), { once: true });
|
||||
});
|
||||
// 所有卡片视频播放
|
||||
cardVideos.forEach(video => {
|
||||
video.play().catch(err => {
|
||||
console.log("卡片视频自动播放失败:", err);
|
||||
container.addEventListener('click', () => video.play(), { once: true });
|
||||
});
|
||||
});
|
||||
}
|
||||
// 向Android发送回调(仅变化时)
|
||||
AndroidBridge.onJsCallback(`HTML已更新宽高:${newWidth} x ${newHeight}(当前语言:${currentLang})`);
|
||||
}
|
||||
|
||||
// ---------------------- 2. 获取并更新网络状态/IP/设备ID ----------------------
|
||||
var networkJson = AndroidBridge.getNetwork();
|
||||
var network = JSON.parse(networkJson);
|
||||
var newDeviceId = network.id || "xxx-xxx-xxxxx";
|
||||
var newIp = network.ip || "0.0.0.0";
|
||||
var newNetworkType = network.type || "NONE";
|
||||
var newNetworkValue = network.value || "";
|
||||
|
||||
// 对比缓存:仅更新变化的参数
|
||||
let hasNetworkChange = false;
|
||||
|
||||
// 设备ID变化
|
||||
if (newDeviceId !== cachedData.deviceId) {
|
||||
console.log("设备ID变化:", cachedData.deviceId + " → " + newDeviceId);
|
||||
cardId.innerText = newDeviceId;
|
||||
cachedData.deviceId = newDeviceId;
|
||||
hasNetworkChange = true;
|
||||
}
|
||||
|
||||
// IP地址变化
|
||||
if(newIp == 'null'){
|
||||
newIp = "0.0.0.0";
|
||||
}
|
||||
if (newIp !== cachedData.ip) {
|
||||
console.log("IP地址变化:", cachedData.ip + " → " + newIp);
|
||||
networkIP.innerText = newIp;
|
||||
cachedData.ip = newIp;
|
||||
hasNetworkChange = true;
|
||||
}
|
||||
|
||||
// 网络状态变化(类型或值变化)
|
||||
if (newNetworkType !== cachedData.networkType || newNetworkValue !== cachedData.networkValue) {
|
||||
console.log("网络状态变化:", cachedData.networkType + "-" + cachedData.networkValue + " → " + newNetworkType + "-" + newNetworkValue);
|
||||
let iconPath = "images/NONE.png";
|
||||
let typeText = i18n[currentLang].unknownNetwork;
|
||||
|
||||
switch (newNetworkType) {
|
||||
case "WIFI":
|
||||
iconPath = "images/WIFI.png";
|
||||
typeText = `WIFI-${newNetworkValue}`;
|
||||
break;
|
||||
case "ETHERNET":
|
||||
iconPath = "images/ETHERNET.png";
|
||||
typeText = currentLang === "zh" ? "有线网络" : "Wired Network";
|
||||
break;
|
||||
case "CELLULAR":
|
||||
iconPath = "images/CELLULAR.png";
|
||||
typeText = `Cellular-${newNetworkValue}`;
|
||||
break;
|
||||
case "NONE":
|
||||
iconPath = "images/NONE.png";
|
||||
typeText = i18n[currentLang].unknownNetwork;
|
||||
break;
|
||||
default:
|
||||
iconPath = "images/NONE.png";
|
||||
typeText = i18n[currentLang].unknownNetwork;
|
||||
}
|
||||
|
||||
// 更新DOM
|
||||
networkIcon.src = iconPath;
|
||||
networkText.innerText = typeText;
|
||||
// 更新缓存
|
||||
cachedData.networkType = newNetworkType;
|
||||
cachedData.networkValue = newNetworkValue;
|
||||
hasNetworkChange = true;
|
||||
}
|
||||
|
||||
// 网络相关有变化时,向Android发送回调
|
||||
if (hasNetworkChange) {
|
||||
AndroidBridge.onJsCallback(`HTML已更新网络信息:ID=${newDeviceId} | IP=${newIp} | 网络=${newNetworkType}-${newNetworkValue}`);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error("定时获取数据失败:", e);
|
||||
// 仅首次失败时显示错误提示(避免重复覆盖)
|
||||
if (loadingTip.style.display !== "none") {
|
||||
loadingTip.innerText = i18n[currentLang].loadFail;
|
||||
container.style.display = "flex";
|
||||
loadingTip.style.display = "none"; //网页调试-周鹏
|
||||
// 默认尺寸
|
||||
// container.style.width = window.innerWidth + "px";
|
||||
// container.style.height = window.innerHeight + "px";
|
||||
if(window.innerWidth> window.innerHeight){
|
||||
container.style.width = "1920px";
|
||||
container.style.height = "1080px";
|
||||
}else{
|
||||
container.style.width = "1080px";
|
||||
container.style.height = "1920px";
|
||||
}
|
||||
setOrientationClass(container);
|
||||
calcRemBase(container);
|
||||
// 初始化缓存
|
||||
// cachedData.width = window.innerWidth + "px";
|
||||
// cachedData.height = window.innerHeight + "px";
|
||||
if (window.innerWidth > window.innerHeight) {
|
||||
container.style.width = "1920px";
|
||||
container.style.height = "1080px";
|
||||
}else{
|
||||
container.style.width = "1080px";
|
||||
container.style.height = "1920px";
|
||||
}
|
||||
|
||||
// 顶部信息栏视频播放
|
||||
infoBarVideo.play().catch(err => {
|
||||
container.addEventListener('click', () => infoBarVideo.play(), { once: true });
|
||||
});
|
||||
cardVideos.forEach(video => {
|
||||
video.play().catch(err => {
|
||||
container.addEventListener('click', () => video.play(), { once: true });
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 7. 页面加载初始化 =====================
|
||||
window.onload = function () {
|
||||
// 1. 初始化语言
|
||||
const currentLang = getCurrentLang();
|
||||
changeLanguage(currentLang);
|
||||
// 2. 首次执行数据获取与更新
|
||||
fetchAndUpdateData();
|
||||
|
||||
// 3. 设置定时任务:每隔5秒执行一次(1000ms=1秒,5000ms=5秒)
|
||||
const intervalTime = 5000;
|
||||
refreshInterval = setInterval(fetchAndUpdateData, intervalTime);
|
||||
console.log(`定时刷新已启动,间隔:${intervalTime}ms`);
|
||||
};
|
||||
|
||||
// ===================== 8. 扩展功能 =====================
|
||||
// 手动切换语言(可选)
|
||||
function toggleLanguage() {
|
||||
const currentLang = document.documentElement.lang;
|
||||
const newLang = currentLang === "zh" ? "en" : "zh";
|
||||
changeLanguage(newLang);
|
||||
}
|
||||
|
||||
// 可选:页面卸载时清除定时任务(避免内存泄漏)
|
||||
window.onunload = function () {
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
console.log("定时刷新已停止");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
/* 初始基准:1920x1080下 1rem=16px */
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 主容器样式 - 移除原背景渐变(改为视频背景) */
|
||||
#redBorderContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1.125rem;
|
||||
padding: 3rem;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
display: none;
|
||||
/* 初始隐藏 */
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
/* 加载中提示 */
|
||||
.loading {
|
||||
font-size: 1.125rem;
|
||||
color: #4a6fa5;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 1.25rem 2.5rem;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 卡片样式:添加相对定位(用于视频背景绝对定位) */
|
||||
.pageDiv {
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 卡片视频背景统一样式(避免内联样式冗余) */
|
||||
.card-bg-video {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* 顶部信息栏视频背景样式 */
|
||||
.info-bar-bg-video {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
z-index: -1;
|
||||
transform: scale(1.05);
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.oneDivContainer {
|
||||
width: 100%;
|
||||
height: 85%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* 三个div的容器样式(默认横屏) */
|
||||
.threeDivContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
/* 通用样式(横竖屏共用) */
|
||||
.pageDiv>div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#redBorderContainer h2 {
|
||||
text-align: left;
|
||||
color: #000000;
|
||||
margin-bottom: 0.9375rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
#redBorderContainer h3 {
|
||||
color: #FFFFFF;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.wifi4GContainer {
|
||||
background-color: #FFFFFF;
|
||||
color: #2d9cdb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.9375rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.fontBold {
|
||||
font-size: 1.375rem;
|
||||
font-weight: 600;
|
||||
color: #2d9cdb;
|
||||
}
|
||||
|
||||
.fontBold2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: #2d9cdb;
|
||||
margin-top: 2%;
|
||||
}
|
||||
|
||||
.Hcenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Wcenter {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.divText {
|
||||
text-align: left;
|
||||
margin-top: 0.875rem;
|
||||
font-size: 1.125rem;
|
||||
color: #FFFFFF;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.divText2 {
|
||||
text-align: left;
|
||||
margin-top: 0.875rem;
|
||||
font-size: 1rem;
|
||||
color: #FFFFFF;
|
||||
line-height: 1.5;
|
||||
background: rgba(45, 156, 219, 0.05);
|
||||
padding: 0.75rem;
|
||||
border-radius: 0.625rem;
|
||||
border-left: 3px solid #2d9cdb;
|
||||
}
|
||||
|
||||
.h2Text {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 网络图标样式 */
|
||||
#networkIcon {
|
||||
width: 1.875rem;
|
||||
height: 1.875rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
#networkText {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 步骤序号样式 */
|
||||
.fontBold .step-icon {
|
||||
color: #27ae60;
|
||||
font-size: 1rem;
|
||||
margin-right: 0.625rem;
|
||||
}
|
||||
|
||||
/* 扫码下载文字样式 */
|
||||
.qrcode-text {
|
||||
font-size: 0.6rem;
|
||||
color: #2d9cdb;
|
||||
font-weight: 600;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* 图片样式优化 */
|
||||
.imgSize1 {
|
||||
border-radius: 0.625rem;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.sPage {
|
||||
margin-top: 15%;
|
||||
margin-left: 12%;
|
||||
width: 76%;
|
||||
height: 65%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------------------------------*/
|
||||
/* 👉 竖屏样式:容器添加 .portrait 类时生效 */
|
||||
#redBorderContainer.portrait .oneDivContainer {
|
||||
height: 90%;
|
||||
}
|
||||
|
||||
#redBorderContainer.portrait .threeDivContainer {
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#redBorderContainer.portrait .pageDiv {
|
||||
width: 100%;
|
||||
height: 33.3%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
position: relative;
|
||||
/* 竖屏模式保持定位 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#redBorderContainer.portrait .imgSize1 {
|
||||
width: 12%;
|
||||
height: 12%;
|
||||
}
|
||||
|
||||
#redBorderContainer.portrait .imgSize2 {
|
||||
width: 5%;
|
||||
height: 5%;
|
||||
}
|
||||
|
||||
#redBorderContainer.portrait .imgSize3 {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
#redBorderContainer.portrait .Wcenter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
#redBorderContainer.portrait .fontBold2 {
|
||||
margin-top: 0%;
|
||||
}
|
||||
|
||||
#redBorderContainer.portrait .sPage {
|
||||
margin-top: 5%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
/* 👉 横屏样式:容器添加 .landscape 类时生效 */
|
||||
#redBorderContainer.landscape .threeDivContainer {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#redBorderContainer.landscape .pageDiv {
|
||||
width: 33.3%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
position: relative;
|
||||
/* 横屏模式保持定位 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#redBorderContainer.landscape .imgSize1 {
|
||||
width: 20%;
|
||||
height: 20%;
|
||||
}
|
||||
|
||||
#redBorderContainer.landscape .imgSize2 {
|
||||
width: 7%;
|
||||
height: 7%;
|
||||
}
|
||||
|
||||
#redBorderContainer.landscape .imgSize3 {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 响应式优化 */
|
||||
@media (max-width: 768px) {
|
||||
.fontBold {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.divText {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.divText2 {
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -13,9 +13,13 @@ import com.xixun.util.PlayerInfo;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
|
||||
import gnph.util.IOs;
|
||||
|
|
@ -102,7 +106,7 @@ public class AIDLService extends Service {
|
|||
public void setUSBProgramPwd(String pwd) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
SimpleDateFormat fmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
|
||||
@SuppressLint("ResourceType")
|
||||
@Override
|
||||
public String executeJosnCommand(String jsonstr) throws RemoteException {
|
||||
|
|
@ -186,17 +190,22 @@ public class AIDLService extends Service {
|
|||
if(t>0) {
|
||||
var len = new File(Util.programDir+"/"+src.filename).length();
|
||||
conn.setHeader("Range", "bytes="+len+"-");
|
||||
Util.println(" Range "+len);
|
||||
Util.println(" Range: bytes="+len+"-");
|
||||
}
|
||||
var input = conn.in();
|
||||
var code = conn.hconn().getResponseCode();
|
||||
var fout = new FileOutputStream(Util.programDir+"/"+src.filename, code==206);
|
||||
IOs.writeCloseIn(fout, input);
|
||||
fout.flush();
|
||||
fout.getFD().sync();
|
||||
fout.close();
|
||||
if(code==200 || code==206) break;
|
||||
else Util.println(" error "+code+" "+conn.hconn().getResponseMessage()+" size: "+new File(src.filename).length()+" "+src.filename);
|
||||
Util.println(" "+code+" - "+conn.hconn().getResponseMessage());
|
||||
if(code==200 || code==206) {
|
||||
var fOut = new FileOutputStream(Util.programDir+"/"+src.filename, code==206);
|
||||
IOs.writeCloseIn(fOut, input);
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
fOut.close();
|
||||
break;
|
||||
} else {
|
||||
Util.println(" body "+IOs.readStrClose(input));
|
||||
Util.println(" size: "+new File(Util.programDir+"/"+src.filename).length()+" "+src.filename);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Util.println(Util.toStr(e));
|
||||
}
|
||||
|
|
@ -247,6 +256,8 @@ public class AIDLService extends Service {
|
|||
try {
|
||||
var fOut = new FileOutputStream(Util.programDir + "/program");
|
||||
fOut.write(jsonBytes);
|
||||
var spaces = " ".getBytes();
|
||||
for(int i=0;i<1000; i++) fOut.write(spaces);
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
fOut.close();
|
||||
|
|
@ -337,6 +348,51 @@ public class AIDLService extends Service {
|
|||
"cardId", Util.getCardId(),
|
||||
"commandId", commandId
|
||||
).toString();
|
||||
} else if(_type.equalsIgnoreCase("GetLog")) {
|
||||
return new JSMap(
|
||||
"_type", "Success",
|
||||
"cardId", Util.getCardId(),
|
||||
"commandId", commandId,
|
||||
"text", Util.buf.toString()
|
||||
).toString();
|
||||
} else if(_type.equalsIgnoreCase("ListProgFiles")) {
|
||||
var files = new File(Util.programDir).listFiles();
|
||||
var writer = new StringBuilder();
|
||||
if(files!=null) {
|
||||
Arrays.sort(files, (f1, f2) -> Long.signum(f2.lastModified() - f1.lastModified()));
|
||||
for(var file : files) writer.append(fmt.format(new Date(file.lastModified()))).append(' ').append(file.getName()).append(' ').append(String.valueOf(file.length())).append('\n');
|
||||
}
|
||||
return new JSMap(
|
||||
"_type", "Success",
|
||||
"cardId", Util.getCardId(),
|
||||
"commandId", commandId,
|
||||
"text", writer.toString()
|
||||
).toString();
|
||||
} else if("GetJson".equalsIgnoreCase(_type)) {
|
||||
var inse = new File(Util.programDir+"/insert");
|
||||
var files = new File(Util.programDir).listFiles();
|
||||
var writer = new StringBuilder();
|
||||
if(inse.isFile()) {
|
||||
var text = IOs.readStrClose(new FileInputStream(inse)).trim();
|
||||
if(files!=null) for(var file : files) if(! "program".equals(file.getName())) text = text.replace("\""+file.getName()+"\"", "\""+file.getName()+"\"/*"+file.length()+" "+fmt.format(new Date(file.lastModified()))+"*/");
|
||||
writer.append("insert:\n");
|
||||
writer.append(text);
|
||||
if(! text.endsWith("\n")) writer.append("\n");
|
||||
}
|
||||
var prog = new File(Util.programDir+"/program");
|
||||
if(! prog.isFile()) writer.append("'program' file not exist");
|
||||
else {
|
||||
var text = IOs.readStrClose(new FileInputStream(prog)).trim();
|
||||
if(files!=null) for(var file : files) if(! "program".equals(file.getName())) text = text.replace("\""+file.getName()+"\"", "\""+file.getName()+"\"/*"+file.length()+" "+fmt.format(new Date(file.lastModified()))+"*/");
|
||||
writer.append("\nprogram:\n");
|
||||
writer.append(text);
|
||||
}
|
||||
return new JSMap(
|
||||
"_type", "Success",
|
||||
"cardId", Util.getCardId(),
|
||||
"commandId", commandId,
|
||||
"text", writer.toString()
|
||||
).toString();
|
||||
}
|
||||
return new JSMap(
|
||||
"_type", "Error",
|
||||
|
|
@ -391,7 +447,7 @@ public class AIDLService extends Service {
|
|||
@Override
|
||||
public String getProgramTask() throws RemoteException {
|
||||
try {
|
||||
return IOs.readStrClose(new FileInputStream(Util.programDir+"/program"));
|
||||
return IOs.readStrClose(new FileInputStream(Util.programDir+"/program")).trim();
|
||||
} catch (Exception e) {
|
||||
Util.printStackTrace(e);
|
||||
return Util.toStr(e);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import android.content.ServiceConnection;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.graphics.Color;
|
||||
import android.media.AudioFocusRequest;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
|
|
@ -19,6 +20,8 @@ import android.os.IBinder;
|
|||
import android.os.StrictMode;
|
||||
import android.view.Choreographer;
|
||||
import android.view.View;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
|
@ -40,6 +43,7 @@ import java.util.LinkedList;
|
|||
import gnph.util.Chsets;
|
||||
import gnph.util.JSList;
|
||||
import gnph.util.JSMap;
|
||||
import gnph.util.O;
|
||||
|
||||
public class MainActivity extends Activity implements Choreographer.FrameCallback {
|
||||
|
||||
|
|
@ -49,9 +53,9 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
public Intent environIntent = new Intent();
|
||||
HashSet<IntentReceiver> environs = new HashSet<>();
|
||||
BackView backView;
|
||||
WebView backViewL;
|
||||
Prog progView, insView;
|
||||
long launchMilli = System.currentTimeMillis();
|
||||
long syncMs;
|
||||
int state;
|
||||
|
||||
@Override
|
||||
|
|
@ -75,7 +79,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
android.Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.MANAGE_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
|
||||
android.Manifest.permission.INTERNET
|
||||
android.Manifest.permission.INTERNET,
|
||||
android.Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
}, 999);
|
||||
}
|
||||
}
|
||||
|
|
@ -136,6 +141,11 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
progView.release();
|
||||
progView = null;
|
||||
}
|
||||
if(backViewL!=null) {
|
||||
backViewL.removeAllViews();
|
||||
backViewL.destroy();
|
||||
backViewL = null;
|
||||
}
|
||||
ins = null;
|
||||
for(var rece : reces) unregisterReceiver(rece);
|
||||
for(var service : services) unbindService(service);
|
||||
|
|
@ -158,23 +168,29 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
protected void onStop() {
|
||||
super.onStop();
|
||||
Util.println(" ==<< MainActivity onStop << "+hashCode());
|
||||
state = 8;
|
||||
stopProg();
|
||||
}
|
||||
@Override
|
||||
protected void onRestart() {
|
||||
super.onRestart();
|
||||
Util.println(" ==>> MainActivity onRestart >> "+hashCode());
|
||||
if(progView==null && insView==null) runOnUiThread(this::initProg);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Util.println(" ==>> MainActivity onResume >> "+hashCode());
|
||||
if(progView==null && insView==null) runOnUiThread(this::initProg);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
Util.println(" ==<< MainActivity onPause << "+hashCode());
|
||||
state = 8;
|
||||
stopProg();
|
||||
}
|
||||
|
||||
CardService serviCard;
|
||||
|
|
@ -186,6 +202,86 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
Util.initDir(this);
|
||||
if(MainService.ins==null) startService(new Intent(this, MainService.class));
|
||||
|
||||
var cardId = Util.getCardId();
|
||||
if(! cardId.isEmpty()) Util.cardType = cardId.toLowerCase().charAt(0);
|
||||
if(Util.cardType=='l') {
|
||||
backViewL = new WebView(this);
|
||||
backViewL.setBackgroundColor(Color.TRANSPARENT);
|
||||
backViewL.setVerticalScrollBarEnabled(false);
|
||||
backViewL.setHorizontalScrollBarEnabled(false);
|
||||
backViewL.setInitialScale(100);
|
||||
backViewL.setPadding(0, 0, 0, 0);
|
||||
backViewL.clearCache(true);
|
||||
|
||||
var settings = backViewL.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setAllowFileAccess(true);
|
||||
settings.setDomStorageEnabled(true);
|
||||
settings.setBuiltInZoomControls(false);
|
||||
settings.setDisplayZoomControls(false);
|
||||
settings.setSupportZoom(false);
|
||||
settings.setUseWideViewPort(true);
|
||||
settings.setLoadsImagesAutomatically(true);
|
||||
settings.setLoadWithOverviewMode(true);
|
||||
// 添加JS交互接口(核心:传递宽高)
|
||||
backViewL.addJavascriptInterface(new Object() {
|
||||
@JavascriptInterface
|
||||
public String getSizeFromAndroid() {
|
||||
if(serviCard!=null && serviCard.asBinder().isBinderAlive()) {
|
||||
try {
|
||||
var www = serviCard.getScreenWidth();
|
||||
var hhh = serviCard.getScreenHeight();
|
||||
if(www!=Util.screenWidth || hhh!=Util.screenHeight) {
|
||||
Util.screenWidth = www;
|
||||
Util.screenHeight = hhh;
|
||||
ins.backView.invalidate();
|
||||
}
|
||||
} catch(Throwable e) {
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
} else {
|
||||
Util.println(" bindService");
|
||||
ins.bindService(ins.intenCard, ins.connCard, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
return "{\"width\":\"" + Util.screenWidth + "px\",\"height\":\"" + Util.screenHeight + "px\"}";
|
||||
}
|
||||
@JavascriptInterface
|
||||
public String getNetwork() {
|
||||
// 获取当前网络类型
|
||||
NetworkTypeUtil.NetworkType networkType = NetworkTypeUtil.getCurrentNetworkType(MainActivity.this);
|
||||
String value = "";
|
||||
switch (networkType) {
|
||||
case WIFI:
|
||||
value = NetworkTypeUtil.getWifiSsid(MainActivity.this);
|
||||
// 处理未获取到SSID的情况
|
||||
if (O.isEmpty(value)) value = "未知WiFi名称";
|
||||
System.out.println("当前是 WiFi 网络:" + value);
|
||||
break;
|
||||
case ETHERNET:
|
||||
System.out.println("当前是有线网络");
|
||||
break;
|
||||
case CELLULAR:
|
||||
// 细分蜂窝网络类型(2G/3G/4G)
|
||||
String cellularSubType = NetworkTypeUtil.getCellularSubType(MainActivity.this);
|
||||
value = cellularSubType;
|
||||
System.out.println("当前是蜂窝网络:" + cellularSubType);
|
||||
break;
|
||||
case NONE:
|
||||
System.out.println("无网络连接");
|
||||
break;
|
||||
case UNKNOWN:
|
||||
System.out.println("未知网络类型(如蓝牙、VPN)");
|
||||
break;
|
||||
}
|
||||
var IP = NetworkTypeUtil.getActiveNetworkIp(MainActivity.this);
|
||||
return "{\"type\":\"" + networkType + "\",\"value\":\""+value+"\",\"ip\":\""+IP+"\",\"id\":\""+cardId+"\"}";
|
||||
}
|
||||
@JavascriptInterface
|
||||
public void onJsCallback(String message) {
|
||||
}
|
||||
}, "AndroidBridge");
|
||||
}
|
||||
|
||||
var cfg = new File(Util.programDir+"/cfg");
|
||||
if(! cfg.isFile()) Util.cfg = new JSMap();
|
||||
else {
|
||||
|
|
@ -235,6 +331,10 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
backView.invalidate();
|
||||
if(Util.isScreenOn) initProg();
|
||||
else state = 8;
|
||||
if(backView.isShown() && backViewL!=null) {
|
||||
setContentView(backViewL);
|
||||
backViewL.loadUrl("file:///android_asset/local_page.html");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Util.makeText(MainActivity.this, Util.toStr(e)).show();
|
||||
Util.printStackTrace(e);
|
||||
|
|
@ -285,17 +385,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
Util.println(" IsScreenOn: "+Util.isScreenOn);
|
||||
if(! Util.isScreenOn) {
|
||||
state = 8;
|
||||
if(insView!=null) {
|
||||
insView.release();
|
||||
insView = null;
|
||||
}
|
||||
if(progView!=null) {
|
||||
progView.release();
|
||||
progView = null;
|
||||
}
|
||||
setContentView(backView);
|
||||
} else if(progView==null && insView==null) MainActivity.ins.runOnUiThread(() -> initProg());
|
||||
System.gc();
|
||||
stopProg();
|
||||
} else if(progView==null && insView==null) runOnUiThread(() -> initProg());
|
||||
}
|
||||
}, new IntentFilter("com.xixun.action.PAUSE_PLAYER"));
|
||||
reces.add(rece);
|
||||
|
|
@ -363,11 +454,11 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
if(! avas.isEmpty()) {
|
||||
var page = avas.get(curAva);
|
||||
for(var layer : page.layers) for(var src : layer.srcs) if(src.isShow) {
|
||||
showHides.add(new MainActivity.ShowHide(ms+1000, src, 'H'));
|
||||
showHides.add(new MainActivity.ShowHide(ms+500, src, 'H'));
|
||||
src.isShow = false;
|
||||
}
|
||||
if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) {
|
||||
showHides.add(new ShowHide(ms + 1000, ()->{
|
||||
showHides.add(new ShowHide(ms + 500, ()->{
|
||||
for(var call : progView.calls) call.doFrame(ms);
|
||||
}));
|
||||
}
|
||||
|
|
@ -390,6 +481,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
root.put("Demand", code);
|
||||
try (var fOut = new FileOutputStream(Util.programDir + "/program")) {
|
||||
root.write(fOut);
|
||||
var spaces = " ".getBytes();
|
||||
for(int i=0;i<1000; i++) fOut.write(spaces);
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
}
|
||||
|
|
@ -478,6 +571,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
AudioFocusRequest audioFocusRequest;
|
||||
public void stopProg() {
|
||||
avas.clear();
|
||||
showHides.clear();
|
||||
showeds.clear();
|
||||
curAva = 0;
|
||||
curTimes = 1;
|
||||
if(insView!=null) {
|
||||
|
|
@ -488,7 +583,11 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
progView.release();
|
||||
progView = null;
|
||||
}
|
||||
setContentView(backView);
|
||||
if(backViewL==null) setContentView(backView);
|
||||
else {
|
||||
setContentView(backViewL);
|
||||
backViewL.loadUrl("file:///android_asset/local_page.html");
|
||||
}
|
||||
System.gc();
|
||||
}
|
||||
public boolean delProgFile() {
|
||||
|
|
@ -498,11 +597,13 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
if(files != null) for(var file : files) if(! file.delete()) ok = false;
|
||||
state = 4;
|
||||
try {
|
||||
var out = new FileOutputStream(Util.programDir+"/program");
|
||||
out.write("{}".getBytes());
|
||||
out.flush();
|
||||
out.getFD().sync();
|
||||
out.close();
|
||||
var fOut = new FileOutputStream(Util.programDir+"/program");
|
||||
fOut.write("{}".getBytes());
|
||||
var spaces = " ".getBytes();
|
||||
for(int i=0;i<1000; i++) fOut.write(spaces);
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
fOut.close();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return ok;
|
||||
|
|
@ -547,7 +648,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
state = 5;
|
||||
Util.println("Init Sync");
|
||||
var ms = System.currentTimeMillis();
|
||||
if(demand==0 || progView==null) syncProg((ms+999)/1000*1000, 0);
|
||||
if(demand==0 || progView==null) syncProg(ms, 0);
|
||||
else {
|
||||
avas.clear();
|
||||
var page = progView.pages.get(demand-1);
|
||||
|
|
@ -609,14 +710,14 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
}
|
||||
public void afterCheck(Prog view, byte[] json) {
|
||||
try {
|
||||
var ms = (System.currentTimeMillis()+999)/1000*1000;
|
||||
var ms = System.currentTimeMillis();
|
||||
if(view.isInsert) {
|
||||
if(insView!=null) insView.release();
|
||||
insView = view;
|
||||
try {
|
||||
var page = avas.get(curAva);
|
||||
for(var layer : page.layers) for(var src : layer.srcs) if(src.isShow) {
|
||||
showHides.add(new ShowHide(ms+1000, src, 'H'));
|
||||
showHides.add(new ShowHide(ms+500, src, 'H'));
|
||||
src.isShow = false;
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
|
@ -628,6 +729,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
}
|
||||
var fOut = new FileOutputStream(Util.programDir + (view.isInsert?"/insert":"/program"));
|
||||
fOut.write(json);
|
||||
var spaces = " ".getBytes();
|
||||
for(int i=0;i<1000; i++) fOut.write(spaces);
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
fOut.close();
|
||||
|
|
@ -671,6 +774,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
}
|
||||
}
|
||||
public LinkedList<ShowHide> showHides = new LinkedList<>();
|
||||
public HashSet<Prog.Source> showeds = new HashSet<>();
|
||||
|
||||
boolean contains(Prog.Source src, char act) {
|
||||
for(var showHide : showHides) if(showHide.src==src && showHide.act==act) return true;
|
||||
return false;
|
||||
|
|
@ -683,17 +788,28 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
while(iter.hasNext()) {
|
||||
var showHide = iter.next();
|
||||
if(showHide.time > milli) {
|
||||
if(showHide.time > milli+250) break;
|
||||
if(showHide.time > milli+400) break;
|
||||
else continue;
|
||||
}
|
||||
if(showHide.act=='H') {
|
||||
showHide.src.hide();
|
||||
showeds.remove(showHide.src);
|
||||
if(! contains(showHide.src, 'S')) showHide.src.release();
|
||||
iter.remove();
|
||||
} else if(showHide.act=='S') {
|
||||
showHide.src.show();
|
||||
showeds.add(showHide.src);
|
||||
iter.remove();
|
||||
} else if(showHide.run!=null) showHide.run.run();
|
||||
} else if(showHide.run!=null) {
|
||||
showHide.run.run();
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
var iterator = showeds.iterator();
|
||||
while(iterator.hasNext()) {
|
||||
var showed = iterator.next();
|
||||
if(showed.view==null || ! showed.view.isShown()) iterator.remove();
|
||||
else showed.doEff();
|
||||
}
|
||||
boolean noProg = progView == null && insView==null;
|
||||
if(noProg && showHides.isEmpty()) {
|
||||
|
|
@ -717,7 +833,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli, showHides);
|
||||
else {
|
||||
for(var layer : lastPage.layers) for(var src : layer.srcs) if(src.isShow) {
|
||||
showHides.add(new ShowHide(lastPage.endMilli+1000, src, 'H'));
|
||||
showHides.add(new ShowHide(lastPage.endMilli+500, src, 'H'));
|
||||
src.isShow = false;
|
||||
}
|
||||
if(isInsert) {
|
||||
|
|
@ -765,7 +881,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
for(int i=0; i<insView.pages.size(); i++) if(insView.pages.get(i).repeatTimes <= 0) {
|
||||
for(var layer : insView.pages.get(i).layers) for(var src : layer.srcs) {
|
||||
var iii = insView;
|
||||
showHides.add(new ShowHide(milli + 1000, ()->{
|
||||
showHides.add(new ShowHide(milli + 500, ()->{
|
||||
iii.removeView(src.view);
|
||||
if(src.view instanceof Choreographer.FrameCallback) iii.calls.remove((Choreographer.FrameCallback) src.view);
|
||||
}));
|
||||
|
|
@ -774,7 +890,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
}
|
||||
if(insView.pages.isEmpty()) {
|
||||
var iii = insView;
|
||||
showHides.add(new ShowHide(milli + 1000, iii::release));
|
||||
showHides.add(new ShowHide(milli + 500, iii::release));
|
||||
insView = null;
|
||||
try {
|
||||
var fOut = new FileOutputStream(Util.programDir + "/insert");
|
||||
|
|
@ -820,7 +936,6 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
start += avas.get(curAva++).tDur;
|
||||
} while(curAva < avas.size() && start<=milli);
|
||||
start -= avas.get(--curAva).tDur;
|
||||
syncMs = milli;
|
||||
Util.println("Sync. dur: "+dur+" milli: "+milli+" start: "+start+" diff: "+(milli - start));
|
||||
}
|
||||
avas.get(curAva).setMillis(start, milli, showHides);
|
||||
|
|
@ -832,7 +947,11 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
|
|||
}
|
||||
waitTo = milli + 1000;
|
||||
if(state!=2) {
|
||||
setContentView(backView);
|
||||
if(backViewL==null) setContentView(backView);
|
||||
else {
|
||||
setContentView(backViewL);
|
||||
backViewL.loadUrl("file:///android_asset/local_page.html");
|
||||
}
|
||||
state = 2;
|
||||
}
|
||||
Util.println("No Avas, back");
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ public class MainService extends Service {
|
|||
else {
|
||||
var fOut = new FileOutputStream(Util.programDir + "/program");
|
||||
fOut.write(json);
|
||||
var spaces = " ".getBytes();
|
||||
for(int i=0;i<1000; i++) fOut.write(spaces);
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
fOut.close();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,283 @@
|
|||
package com.xixun.xixunplayer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* 网络类型判断工具类(Java 版本,兼容低编译版本,无需 API 29)
|
||||
*/
|
||||
public class NetworkTypeUtil {
|
||||
|
||||
/**
|
||||
* 网络类型枚举
|
||||
*/
|
||||
public enum NetworkType {
|
||||
WIFI, // WiFi 网络
|
||||
ETHERNET, // 有线网络(网线、USB 共享等)
|
||||
CELLULAR, // 蜂窝网络(2G/3G/4G)
|
||||
NONE, // 无网络
|
||||
UNKNOWN // 未知网络(蓝牙、VPN 等)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前网络类型(兼容 Android 16+,不依赖 API 29)
|
||||
* @param context 上下文(建议用 Application 上下文,避免内存泄漏)
|
||||
*/
|
||||
public static NetworkType getCurrentNetworkType(Context context) {
|
||||
// 获取 ConnectivityManager 系统服务
|
||||
ConnectivityManager connectivityManager =
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
// Android 6.0+(API 23+):优先使用 NetworkCapabilities(更准确)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
NetworkCapabilities networkCapabilities =
|
||||
connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork());
|
||||
|
||||
if (networkCapabilities != null) {
|
||||
// 有线网络(TRANSPORT_ETHERNET)
|
||||
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
|
||||
return NetworkType.ETHERNET;
|
||||
}
|
||||
// WiFi 网络(TRANSPORT_WIFI)
|
||||
else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
||||
return NetworkType.WIFI;
|
||||
}
|
||||
// 蜂窝网络(TRANSPORT_CELLULAR)
|
||||
else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
||||
return NetworkType.CELLULAR;
|
||||
}
|
||||
// 其他未知网络(蓝牙、VPN 等)
|
||||
else {
|
||||
return NetworkType.UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
// 无活跃网络
|
||||
return NetworkType.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Android 5.0-(API <23):兼容旧 API(已废弃,仅作兜底)
|
||||
@SuppressWarnings("deprecation")
|
||||
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
|
||||
if (networkInfo != null && networkInfo.isConnected()) {
|
||||
switch (networkInfo.getType()) {
|
||||
case ConnectivityManager.TYPE_WIFI:
|
||||
return NetworkType.WIFI;
|
||||
case ConnectivityManager.TYPE_ETHERNET:
|
||||
return NetworkType.ETHERNET;
|
||||
case ConnectivityManager.TYPE_MOBILE:
|
||||
return NetworkType.CELLULAR;
|
||||
default:
|
||||
return NetworkType.UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
// 无网络连接
|
||||
return NetworkType.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助方法:判断是否为 WiFi 网络
|
||||
*/
|
||||
public static boolean isWifi(Context context) {
|
||||
return getCurrentNetworkType(context) == NetworkType.WIFI;
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助方法:判断是否为有线网络
|
||||
*/
|
||||
public static boolean isEthernet(Context context) {
|
||||
return getCurrentNetworkType(context) == NetworkType.ETHERNET;
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助方法:判断是否为蜂窝网络
|
||||
*/
|
||||
public static boolean isCellular(Context context) {
|
||||
return getCurrentNetworkType(context) == NetworkType.CELLULAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助方法:判断是否有网络连接(WiFi/有线/蜂窝均可)
|
||||
*/
|
||||
public static boolean isNetworkAvailable(Context context) {
|
||||
return getCurrentNetworkType(context) != NetworkType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 细分蜂窝网络类型(2G/3G/4G,兼容低版本,无 5G 判断)
|
||||
*/
|
||||
public static String getCellularSubType(Context context) {
|
||||
TelephonyManager telephonyManager =
|
||||
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
|
||||
// 低版本统一使用旧 API(避免依赖高版本常量)
|
||||
@SuppressWarnings("deprecation")
|
||||
int networkType = telephonyManager.getNetworkType();
|
||||
|
||||
switch (networkType) {
|
||||
// 4G 网络(LTE)
|
||||
case TelephonyManager.NETWORK_TYPE_LTE:
|
||||
return "4G";
|
||||
// 3G 网络(HSPA 系列)
|
||||
case TelephonyManager.NETWORK_TYPE_HSPA:
|
||||
case TelephonyManager.NETWORK_TYPE_HSDPA:
|
||||
case TelephonyManager.NETWORK_TYPE_HSUPA:
|
||||
case TelephonyManager.NETWORK_TYPE_HSPAP:
|
||||
case TelephonyManager.NETWORK_TYPE_UMTS:
|
||||
return "3G";
|
||||
// 2G 网络(GSM/CDMA 等)
|
||||
case TelephonyManager.NETWORK_TYPE_GSM:
|
||||
case TelephonyManager.NETWORK_TYPE_CDMA:
|
||||
case TelephonyManager.NETWORK_TYPE_EDGE:
|
||||
case TelephonyManager.NETWORK_TYPE_1xRTT:
|
||||
return "2G";
|
||||
// 其他未知蜂窝网络
|
||||
default:
|
||||
return "未知蜂窝网络";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增:获取当前连接的WiFi名称(SSID)
|
||||
* @param context 上下文
|
||||
* @return WiFi名称(未连接则返回空字符串)
|
||||
*/
|
||||
public static String getWifiSsid(Context context) {
|
||||
try {
|
||||
// 获取WiFi管理器(使用Application上下文,避免内存泄漏)
|
||||
WifiManager wifiManager = (WifiManager) context.getApplicationContext()
|
||||
.getSystemService(Context.WIFI_SERVICE);
|
||||
|
||||
if (wifiManager == null) {
|
||||
return ""; // 设备不支持WiFi
|
||||
}
|
||||
|
||||
// 获取WiFi连接信息(低版本兼容)
|
||||
@SuppressWarnings("deprecation")
|
||||
android.net.wifi.WifiInfo wifiInfo = wifiManager.getConnectionInfo();
|
||||
|
||||
if (wifiInfo == null) {
|
||||
return ""; // 未连接WiFi
|
||||
}
|
||||
|
||||
String ssid = wifiInfo.getSSID();
|
||||
if (TextUtils.isEmpty(ssid)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 处理SSID格式:部分设备返回带双引号(如"MyWiFi"),去除引号
|
||||
if (ssid.startsWith("\"") && ssid.endsWith("\"")) {
|
||||
ssid = ssid.substring(1, ssid.length() - 1);
|
||||
}
|
||||
|
||||
// 排除无效SSID(如"<unknown ssid>")
|
||||
return "unknown ssid".equalsIgnoreCase(ssid) ? "" : ssid;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getActiveNetworkIp(Context context) {
|
||||
// 优先通过ConnectivityManager判断活动网络类型
|
||||
String networkType = getActiveNetworkType(context);
|
||||
|
||||
try {
|
||||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||
while (interfaces.hasMoreElements()) {
|
||||
NetworkInterface ni = interfaces.nextElement();
|
||||
|
||||
// 根据网络类型过滤接口
|
||||
if (!isValidInterface(ni, networkType)) continue;
|
||||
|
||||
Enumeration<InetAddress> addresses = ni.getInetAddresses();
|
||||
while (addresses.hasMoreElements()) {
|
||||
InetAddress addr = addresses.nextElement();
|
||||
if (!addr.isLoopbackAddress() && addr instanceof Inet4Address) {
|
||||
String ip = addr.getHostAddress();
|
||||
// 排除热点默认IP
|
||||
if (!ip.startsWith("192.168.43.")) {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null; // 未找到有效IP
|
||||
}
|
||||
|
||||
// 判断活动网络类型
|
||||
private static String getActiveNetworkType(Context context) {
|
||||
ConnectivityManager cm = (ConnectivityManager)
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Network network = cm.getActiveNetwork();
|
||||
if (network == null) return "NONE";
|
||||
|
||||
NetworkCapabilities capabilities = cm.getNetworkCapabilities(network);
|
||||
if (capabilities == null) return "NONE";
|
||||
|
||||
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
||||
return "WIFI";
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
|
||||
return "ETHERNET";
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
||||
return "CELLULAR";
|
||||
}
|
||||
} else {
|
||||
// 兼容旧版本
|
||||
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
|
||||
if (activeNetwork != null) {
|
||||
if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
|
||||
return "WIFI";
|
||||
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_ETHERNET) {
|
||||
return "ETHERNET";
|
||||
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
|
||||
return "CELLULAR";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "NONE";
|
||||
}
|
||||
|
||||
// 根据网络类型验证接口
|
||||
private static boolean isValidInterface(NetworkInterface ni, String networkType) {
|
||||
try {
|
||||
// 跳过未激活或回环接口
|
||||
if (!ni.isUp() || ni.isLoopback()) return false;
|
||||
|
||||
switch (networkType) {
|
||||
case "WIFI":
|
||||
return ni.getName().contains("wlan");
|
||||
case "ETHERNET":
|
||||
return ni.getName().startsWith("eth") || ni.getName().startsWith("usb");
|
||||
case "CELLULAR":
|
||||
return ni.getName().contains("rmnet") || ni.getName().contains("ppp");
|
||||
default:
|
||||
return true; // 未知类型不过滤
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -99,6 +99,7 @@ public class Prog extends AbsLayout {
|
|||
page.repeatTimes = pageMap.intg("repeatTimes", 1);
|
||||
page.parse(pageMap.jslist("schedules"));
|
||||
var waitAudio = pageMap.bool("waitAudio");
|
||||
var zzz = 0;
|
||||
HashMap<String, Prog.Source> videoMap = new HashMap<>();
|
||||
for(int ll=layers.size()-1; ll>=0; ll--) {
|
||||
var layer = new Layer();
|
||||
|
|
@ -112,6 +113,7 @@ public class Prog extends AbsLayout {
|
|||
bdWidth = bdEle.img.getHeight();
|
||||
}
|
||||
var src = new Source();
|
||||
src.z = zzz++;
|
||||
for(var source : sources) {
|
||||
src.type = source.stnn("_type");
|
||||
if(src.type.isEmpty()) continue;
|
||||
|
|
@ -271,10 +273,11 @@ public class Prog extends AbsLayout {
|
|||
src.box = source.bool("isPreSplit") ? this : box;
|
||||
layer.srcs.add(src);
|
||||
src = new Source();
|
||||
src.z = zzz++;
|
||||
}
|
||||
} else if(src.type.equals("Audio")) {
|
||||
if(id==null) continue;
|
||||
src.typ = 'A';
|
||||
src.typ = 'V';
|
||||
src.path = Util.programDir + "/" +id;
|
||||
src.vol = source.intg("vol", 100) / 100.0f;
|
||||
src.dur = dur;
|
||||
|
|
@ -282,6 +285,7 @@ public class Prog extends AbsLayout {
|
|||
src.box = box;
|
||||
layer.srcs.add(src);
|
||||
src = new Source();
|
||||
src.z = zzz++;
|
||||
} else if(src.type.equals("Scroll")) {
|
||||
JSList<String> imgs = source.jslist("imgs");
|
||||
if(imgs.isEmpty()) continue;
|
||||
|
|
@ -365,9 +369,11 @@ public class Prog extends AbsLayout {
|
|||
src.view = imgView;
|
||||
src.view.setVisibility(GONE);
|
||||
src.view.setLayoutParams(geo);
|
||||
src.view.setZ(src.z);
|
||||
box.addView(src.view);
|
||||
layer.srcs.add(src);
|
||||
src = new Source();
|
||||
src.z = zzz++;
|
||||
}
|
||||
}
|
||||
} else if(src.type.equals("DigitalClock")) src.view = new SrcDigitalClock(this, source);
|
||||
|
|
@ -438,9 +444,11 @@ public class Prog extends AbsLayout {
|
|||
if(src.view==null) continue;
|
||||
src.view.setVisibility(GONE);
|
||||
src.view.setLayoutParams(geo);
|
||||
src.view.setZ(src.z);
|
||||
(source.bool("isPreSplit") ? this : box).addView(src.view);
|
||||
layer.srcs.add(src);
|
||||
src = new Source();
|
||||
src.z = zzz++;
|
||||
}
|
||||
if(bdEle!=null && bdStart < bdEnd) {
|
||||
JSList<Long> geometry = border.jslist("geometry");
|
||||
|
|
@ -449,6 +457,7 @@ public class Prog extends AbsLayout {
|
|||
src.rotate = (float) border.dbl("rotate");
|
||||
src.view = bdEle;
|
||||
src.view.setVisibility(GONE);
|
||||
src.view.setZ(src.z);
|
||||
box.addView(src.view, new AbsLayout.LayoutParams(geometry.get(0).intValue(), geometry.get(1).intValue(), geometry.get(2).intValue(), geometry.get(3).intValue()));
|
||||
layer.srcs.add(src);
|
||||
}
|
||||
|
|
@ -516,7 +525,7 @@ public class Prog extends AbsLayout {
|
|||
AbsLayout box;
|
||||
String path;
|
||||
float vol;
|
||||
int dur;
|
||||
int dur, z;
|
||||
boolean useSW;
|
||||
|
||||
public Source() {
|
||||
|
|
@ -528,7 +537,8 @@ public class Prog extends AbsLayout {
|
|||
void show() {
|
||||
if(typ=='V') {
|
||||
if(alphaShow==1) return;
|
||||
((SrcVideo)view).ijkPlayer.start();
|
||||
var video = (SrcVideo) view;
|
||||
video.ijkPlayer.start();
|
||||
view.setAlpha((alphaShow = 1) * alpha);
|
||||
} else {
|
||||
if(view.getVisibility()==VISIBLE) return;
|
||||
|
|
@ -538,7 +548,6 @@ public class Prog extends AbsLayout {
|
|||
if(isEntryRand) entryEff = Effect.values()[Util.rand.nextInt(Effect.values().length)];
|
||||
if(isExitRand) exitEff = Effect.values()[Util.rand.nextInt(Effect.values().length)];
|
||||
resetEff();
|
||||
doEff();
|
||||
if(tts!=null) tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, null);
|
||||
}
|
||||
void hide() {
|
||||
|
|
@ -554,17 +563,24 @@ public class Prog extends AbsLayout {
|
|||
if(view instanceof GifImageView) ((GifImageView) view).setImageURI(uri);
|
||||
if(tts!=null && tts.isSpeaking()) tts.stop();
|
||||
}
|
||||
void prepare(long seek) {
|
||||
var ms = System.currentTimeMillis();
|
||||
void prepare(List<MainActivity.ShowHide> shows, long seek) {
|
||||
if(view==null) {
|
||||
var video = new SrcVideo(box.getContext(), path, vol, dur, seek, useSW);
|
||||
video.setLayoutParams(geo);
|
||||
video.setAlpha(alphaShow = 0);
|
||||
video.setVisibility(VISIBLE);
|
||||
video.setZ(z);
|
||||
box.addView(view = video);
|
||||
shows.add(new MainActivity.ShowHide(startMilli+450, ()->{
|
||||
if(video.ijkPlayer==null) Util.println(" ijkPlayer null");
|
||||
else {
|
||||
video.ijkPlayer.pause();
|
||||
video.ijkPlayer.seekTo(seek<1000 ? 0 : seek);
|
||||
}
|
||||
}));
|
||||
}
|
||||
ms = System.currentTimeMillis() - ms;
|
||||
Util.println(" prepare ms "+ms);
|
||||
shows.add(new MainActivity.ShowHide(startMilli+500, this, 'S'));
|
||||
isShow = true;
|
||||
}
|
||||
void release() {
|
||||
if(view instanceof SrcVideo) {
|
||||
|
|
@ -574,6 +590,7 @@ public class Prog extends AbsLayout {
|
|||
}
|
||||
}
|
||||
void doEff() {
|
||||
if(view==null) return;
|
||||
var w = view.getLayoutParams().width;
|
||||
var h = view.getLayoutParams().height;
|
||||
if(ff < entryDur) {
|
||||
|
|
@ -754,11 +771,7 @@ public class Prog extends AbsLayout {
|
|||
for(var src : layer.srcs) {
|
||||
src.endMilli = start + src.endTime;
|
||||
src.startMilli = start + src.startTime;
|
||||
if(src.startTime == 0) {
|
||||
src.prepare(cur - src.startMilli);
|
||||
shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S'));
|
||||
src.isShow = true;
|
||||
}
|
||||
if(src.startTime == 0) src.prepare(shows, cur - src.startMilli);
|
||||
}
|
||||
}
|
||||
if(Util.logOn) {
|
||||
|
|
@ -778,14 +791,11 @@ public class Prog extends AbsLayout {
|
|||
for(var src : layer.srcs) {
|
||||
if(src.isShow) {
|
||||
if(milli >= src.endMilli) {
|
||||
shows.add(new MainActivity.ShowHide(src.endMilli+1000, src, 'H'));
|
||||
shows.add(new MainActivity.ShowHide(src.endMilli+500, src, 'H'));
|
||||
src.isShow = false;
|
||||
}
|
||||
else src.doEff();
|
||||
} else if(milli < src.endMilli && milli >= src.startMilli) {
|
||||
src.prepare(milli - src.startMilli);
|
||||
shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S'));
|
||||
src.isShow = true;
|
||||
src.prepare(shows, milli - src.startMilli);
|
||||
}
|
||||
}
|
||||
if(milli >= layer.endMilli) {
|
||||
|
|
@ -793,14 +803,7 @@ public class Prog extends AbsLayout {
|
|||
for(var src : layer.srcs) {
|
||||
src.endMilli += layer.dur;
|
||||
src.startMilli += layer.dur;
|
||||
if(src.startTime > 0) {
|
||||
shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'H'));
|
||||
src.isShow = false;
|
||||
} else {
|
||||
src.prepare(0);
|
||||
shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S'));
|
||||
src.isShow = true;
|
||||
}
|
||||
if(src.startTime == 0) src.prepare(shows, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL
|
|||
super(context);
|
||||
this.vol = vol;
|
||||
setSurfaceTextureListener(this);
|
||||
Util.println(" video new");
|
||||
ijkPlayer = new IjkMediaPlayer();
|
||||
if(! useSW) {
|
||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1);
|
||||
|
|
@ -41,7 +40,10 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL
|
|||
ijkPlayer.setOnPreparedListener(null);
|
||||
if(getAlpha() < 0.25) {
|
||||
ijkPlayer.pause();
|
||||
ijkPlayer.seekTo(seek<500 ? 0 : seek);
|
||||
var aaa = seek<1000 ? 0 : seek;
|
||||
ijkPlayer.seekTo(aaa);
|
||||
ijkPlayer.pause();
|
||||
ijkPlayer.seekTo(aaa);
|
||||
} else if(seek>=1000) ijkPlayer.seekTo(seek);
|
||||
bitRate = ijkPlayer.getBitRate();
|
||||
var diff = dur - ijkPlayer.getDuration();
|
||||
|
|
@ -77,26 +79,13 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL
|
|||
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {}
|
||||
|
||||
void release() {
|
||||
Util.println(" video releasing");
|
||||
if(ijkPlayer!=null) {
|
||||
ijkPlayer.release();
|
||||
ijkPlayer = null;
|
||||
}
|
||||
Util.println(" surface releasing");
|
||||
if(surface!=null) {
|
||||
surface.release();
|
||||
surface = null;
|
||||
}
|
||||
Util.println(" surface released");
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onVisibilityAggregated(boolean isVisible) {
|
||||
// super.onVisibilityAggregated(isVisible);
|
||||
// if(isVisible) ijkPlayer.start();
|
||||
// else {
|
||||
// ijkPlayer.pause();
|
||||
// ijkPlayer.seekTo(0);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ public class SrcWeb extends WebView implements Choreographer.FrameCallback {
|
|||
});
|
||||
loadUrl(url);
|
||||
refresh = json.intg("refreshSec")*1000;
|
||||
if(refresh==0 || Util.custom!=Util.Custom.Yishi) this.calls = prog.calls;
|
||||
if(refresh==0 && Util.custom!=Util.Custom.Yishi) this.calls = prog.calls;
|
||||
prog.calls.add(this);
|
||||
nextMs = System.currentTimeMillis() + refresh;
|
||||
}
|
||||
|
|
@ -111,8 +111,10 @@ public class SrcWeb extends WebView implements Choreographer.FrameCallback {
|
|||
loadUrl(url);
|
||||
Util.println(" WebView Refreshed");
|
||||
} else if(calls!=null) {
|
||||
calls.remove(this);
|
||||
calls = null;
|
||||
var calls = this.calls;
|
||||
this.calls = null;
|
||||
var acti = MainActivity.ins;
|
||||
if(acti!=null) acti.showHides.add(new MainActivity.ShowHide(0, () -> calls.remove(this)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,6 +141,8 @@ public class TCPThread extends Thread {
|
|||
else {
|
||||
var fOut = new FileOutputStream(Util.programDir + "/program");
|
||||
fOut.write(json);
|
||||
var spaces = " ".getBytes();
|
||||
for(int i=0;i<1000; i++) fOut.write(spaces);
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
fOut.close();
|
||||
|
|
@ -191,6 +193,8 @@ public class TCPThread extends Thread {
|
|||
else {
|
||||
var fOut = new FileOutputStream(Util.programDir + "/program");
|
||||
fOut.write(json);
|
||||
fOut.write("{}".getBytes());
|
||||
var spaces = " ".getBytes();
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
fOut.close();
|
||||
|
|
@ -275,7 +279,6 @@ public class TCPThread extends Thread {
|
|||
writer.append(" Size Avas: ").append(String.valueOf(main.avas.size())).append(" Pages: ").append(main.progView==null ? "null" : String.valueOf(main.progView.pages.size()));
|
||||
writer.append("\n");
|
||||
writer.append(" Launch: ").append(fmt.format(main.launchMilli)).append("\n");
|
||||
writer.append(" Sync: ").append(fmt.format(main.syncMs)).append("\n");
|
||||
if(main.insView==null) writer.append(" InseView is Null\n");
|
||||
if(main.progView==null) writer.append(" ProgView is Null\n");
|
||||
if(main.avas.isEmpty()) writer.append(" No Avas\n");
|
||||
|
|
@ -309,6 +312,8 @@ public class TCPThread extends Thread {
|
|||
writer.append("\n\n");
|
||||
writer.append(Util.cfg.toStr());
|
||||
writer.append("\n\n");
|
||||
writer.append("showeds "+main.showeds.size()).append(" showHides "+main.showHides.size());
|
||||
writer.append("\n\n");
|
||||
|
||||
var latch = new CountDownLatch(1);
|
||||
if(main!=null) main.runOnUiThread(() -> {
|
||||
|
|
@ -376,7 +381,7 @@ public class TCPThread extends Thread {
|
|||
var inse = new File(Util.programDir+"/insert");
|
||||
var files = new File(Util.programDir).listFiles();
|
||||
if(inse.isFile()) {
|
||||
var json = IOs.readStrClose(new FileInputStream(inse));
|
||||
var json = IOs.readStrClose(new FileInputStream(inse)).trim();
|
||||
if(files!=null) for(var file : files) if(! "program".equals(file.getName())) json = json.replace("\""+file.getName()+"\"", "\""+file.getName()+"\"/*"+file.length()+" "+fmt.format(new Date(file.lastModified()))+"*/");
|
||||
var writer = new OutputStreamWriter(out);
|
||||
writer.append("insert:\n");
|
||||
|
|
@ -387,7 +392,7 @@ public class TCPThread extends Thread {
|
|||
var prog = new File(Util.programDir+"/program");
|
||||
if(! prog.isFile()) new JSMap("msg", "'program' file not exist").write(out);
|
||||
else {
|
||||
var json = IOs.readStrClose(new FileInputStream(prog));
|
||||
var json = IOs.readStrClose(new FileInputStream(prog)).trim();
|
||||
if(files!=null) for(var file : files) if(! "program".equals(file.getName())) json = json.replace("\""+file.getName()+"\"", "\""+file.getName()+"\"/*"+file.length()+" "+fmt.format(new Date(file.lastModified()))+"*/");
|
||||
var writer = new OutputStreamWriter(out);
|
||||
writer.append("\nprogram:\n");
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public class Util {
|
|||
public static volatile long downId;
|
||||
public static int screenWidth = 1920, screenHeight = 1080;
|
||||
public static double lat, lng;
|
||||
public static char cardType = 0;
|
||||
public static boolean isScreenOn, isAudioGain, logOn;
|
||||
|
||||
public static void initDir(Context ctx) {
|
||||
|
|
|
|||