This commit is contained in:
Gangphon 2025-12-23 16:40:05 +08:00
parent 4a3e4360bd
commit a8dc0a192f
30 changed files with 1447 additions and 89 deletions

View File

@ -11,7 +11,7 @@ android {
minSdk 21 minSdk 21
targetSdk 34 targetSdk 34
versionCode 1 versionCode 1
versionName "2.2.4-N" versionName "2.2.16-N"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }

View File

@ -10,6 +10,11 @@
android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <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" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View 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>

View File

@ -13,9 +13,13 @@ import com.xixun.util.PlayerInfo;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import gnph.util.IOs; import gnph.util.IOs;
@ -102,7 +106,7 @@ public class AIDLService extends Service {
public void setUSBProgramPwd(String pwd) throws RemoteException { public void setUSBProgramPwd(String pwd) throws RemoteException {
} }
SimpleDateFormat fmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
@SuppressLint("ResourceType") @SuppressLint("ResourceType")
@Override @Override
public String executeJosnCommand(String jsonstr) throws RemoteException { public String executeJosnCommand(String jsonstr) throws RemoteException {
@ -186,17 +190,22 @@ public class AIDLService extends Service {
if(t>0) { if(t>0) {
var len = new File(Util.programDir+"/"+src.filename).length(); var len = new File(Util.programDir+"/"+src.filename).length();
conn.setHeader("Range", "bytes="+len+"-"); conn.setHeader("Range", "bytes="+len+"-");
Util.println(" Range "+len); Util.println(" Range: bytes="+len+"-");
} }
var input = conn.in(); var input = conn.in();
var code = conn.hconn().getResponseCode(); var code = conn.hconn().getResponseCode();
var fout = new FileOutputStream(Util.programDir+"/"+src.filename, code==206); Util.println(" "+code+" - "+conn.hconn().getResponseMessage());
IOs.writeCloseIn(fout, input); if(code==200 || code==206) {
fout.flush(); var fOut = new FileOutputStream(Util.programDir+"/"+src.filename, code==206);
fout.getFD().sync(); IOs.writeCloseIn(fOut, input);
fout.close(); fOut.flush();
if(code==200 || code==206) break; fOut.getFD().sync();
else Util.println(" error "+code+" "+conn.hconn().getResponseMessage()+" size: "+new File(src.filename).length()+" "+src.filename); 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) { } catch (Throwable e) {
Util.println(Util.toStr(e)); Util.println(Util.toStr(e));
} }
@ -247,6 +256,8 @@ public class AIDLService extends Service {
try { try {
var fOut = new FileOutputStream(Util.programDir + "/program"); var fOut = new FileOutputStream(Util.programDir + "/program");
fOut.write(jsonBytes); fOut.write(jsonBytes);
var spaces = " ".getBytes();
for(int i=0;i<1000; i++) fOut.write(spaces);
fOut.flush(); fOut.flush();
fOut.getFD().sync(); fOut.getFD().sync();
fOut.close(); fOut.close();
@ -337,6 +348,51 @@ public class AIDLService extends Service {
"cardId", Util.getCardId(), "cardId", Util.getCardId(),
"commandId", commandId "commandId", commandId
).toString(); ).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( return new JSMap(
"_type", "Error", "_type", "Error",
@ -391,7 +447,7 @@ public class AIDLService extends Service {
@Override @Override
public String getProgramTask() throws RemoteException { public String getProgramTask() throws RemoteException {
try { try {
return IOs.readStrClose(new FileInputStream(Util.programDir+"/program")); return IOs.readStrClose(new FileInputStream(Util.programDir+"/program")).trim();
} catch (Exception e) { } catch (Exception e) {
Util.printStackTrace(e); Util.printStackTrace(e);
return Util.toStr(e); return Util.toStr(e);

View File

@ -11,6 +11,7 @@ import android.content.ServiceConnection;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Color;
import android.media.AudioFocusRequest; import android.media.AudioFocusRequest;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Build; import android.os.Build;
@ -19,6 +20,8 @@ import android.os.IBinder;
import android.os.StrictMode; import android.os.StrictMode;
import android.view.Choreographer; import android.view.Choreographer;
import android.view.View; import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
@ -40,6 +43,7 @@ import java.util.LinkedList;
import gnph.util.Chsets; import gnph.util.Chsets;
import gnph.util.JSList; import gnph.util.JSList;
import gnph.util.JSMap; import gnph.util.JSMap;
import gnph.util.O;
public class MainActivity extends Activity implements Choreographer.FrameCallback { 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(); public Intent environIntent = new Intent();
HashSet<IntentReceiver> environs = new HashSet<>(); HashSet<IntentReceiver> environs = new HashSet<>();
BackView backView; BackView backView;
WebView backViewL;
Prog progView, insView; Prog progView, insView;
long launchMilli = System.currentTimeMillis(); long launchMilli = System.currentTimeMillis();
long syncMs;
int state; int state;
@Override @Override
@ -75,7 +79,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, android.Manifest.permission.MANAGE_EXTERNAL_STORAGE,
android.Manifest.permission.RECEIVE_BOOT_COMPLETED, android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
android.Manifest.permission.INTERNET android.Manifest.permission.INTERNET,
android.Manifest.permission.ACCESS_COARSE_LOCATION
}, 999); }, 999);
} }
} }
@ -136,6 +141,11 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
progView.release(); progView.release();
progView = null; progView = null;
} }
if(backViewL!=null) {
backViewL.removeAllViews();
backViewL.destroy();
backViewL = null;
}
ins = null; ins = null;
for(var rece : reces) unregisterReceiver(rece); for(var rece : reces) unregisterReceiver(rece);
for(var service : services) unbindService(service); for(var service : services) unbindService(service);
@ -158,23 +168,29 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
Util.println(" ==<< MainActivity onStop << "+hashCode()); Util.println(" ==<< MainActivity onStop << "+hashCode());
state = 8;
stopProg();
} }
@Override @Override
protected void onRestart() { protected void onRestart() {
super.onRestart(); super.onRestart();
Util.println(" ==>> MainActivity onRestart >> "+hashCode()); Util.println(" ==>> MainActivity onRestart >> "+hashCode());
if(progView==null && insView==null) runOnUiThread(this::initProg);
} }
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
Util.println(" ==>> MainActivity onResume >> "+hashCode()); Util.println(" ==>> MainActivity onResume >> "+hashCode());
if(progView==null && insView==null) runOnUiThread(this::initProg);
} }
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
Util.println(" ==<< MainActivity onPause << "+hashCode()); Util.println(" ==<< MainActivity onPause << "+hashCode());
state = 8;
stopProg();
} }
CardService serviCard; CardService serviCard;
@ -186,6 +202,86 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
Util.initDir(this); Util.initDir(this);
if(MainService.ins==null) startService(new Intent(this, MainService.class)); 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"); var cfg = new File(Util.programDir+"/cfg");
if(! cfg.isFile()) Util.cfg = new JSMap(); if(! cfg.isFile()) Util.cfg = new JSMap();
else { else {
@ -235,6 +331,10 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
backView.invalidate(); backView.invalidate();
if(Util.isScreenOn) initProg(); if(Util.isScreenOn) initProg();
else state = 8; else state = 8;
if(backView.isShown() && backViewL!=null) {
setContentView(backViewL);
backViewL.loadUrl("file:///android_asset/local_page.html");
}
} catch (Exception e) { } catch (Exception e) {
Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.makeText(MainActivity.this, Util.toStr(e)).show();
Util.printStackTrace(e); Util.printStackTrace(e);
@ -285,17 +385,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
Util.println(" IsScreenOn: "+Util.isScreenOn); Util.println(" IsScreenOn: "+Util.isScreenOn);
if(! Util.isScreenOn) { if(! Util.isScreenOn) {
state = 8; state = 8;
if(insView!=null) { stopProg();
insView.release(); } else if(progView==null && insView==null) runOnUiThread(() -> initProg());
insView = null;
}
if(progView!=null) {
progView.release();
progView = null;
}
setContentView(backView);
} else if(progView==null && insView==null) MainActivity.ins.runOnUiThread(() -> initProg());
System.gc();
} }
}, new IntentFilter("com.xixun.action.PAUSE_PLAYER")); }, new IntentFilter("com.xixun.action.PAUSE_PLAYER"));
reces.add(rece); reces.add(rece);
@ -363,11 +454,11 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
if(! avas.isEmpty()) { if(! avas.isEmpty()) {
var page = avas.get(curAva); var page = avas.get(curAva);
for(var layer : page.layers) for(var src : layer.srcs) if(src.isShow) { 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; src.isShow = false;
} }
if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) { 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); for(var call : progView.calls) call.doFrame(ms);
})); }));
} }
@ -390,6 +481,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
root.put("Demand", code); root.put("Demand", code);
try (var fOut = new FileOutputStream(Util.programDir + "/program")) { try (var fOut = new FileOutputStream(Util.programDir + "/program")) {
root.write(fOut); root.write(fOut);
var spaces = " ".getBytes();
for(int i=0;i<1000; i++) fOut.write(spaces);
fOut.flush(); fOut.flush();
fOut.getFD().sync(); fOut.getFD().sync();
} }
@ -478,6 +571,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
AudioFocusRequest audioFocusRequest; AudioFocusRequest audioFocusRequest;
public void stopProg() { public void stopProg() {
avas.clear(); avas.clear();
showHides.clear();
showeds.clear();
curAva = 0; curAva = 0;
curTimes = 1; curTimes = 1;
if(insView!=null) { if(insView!=null) {
@ -488,7 +583,11 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
progView.release(); progView.release();
progView = null; progView = null;
} }
setContentView(backView); if(backViewL==null) setContentView(backView);
else {
setContentView(backViewL);
backViewL.loadUrl("file:///android_asset/local_page.html");
}
System.gc(); System.gc();
} }
public boolean delProgFile() { 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; if(files != null) for(var file : files) if(! file.delete()) ok = false;
state = 4; state = 4;
try { try {
var out = new FileOutputStream(Util.programDir+"/program"); var fOut = new FileOutputStream(Util.programDir+"/program");
out.write("{}".getBytes()); fOut.write("{}".getBytes());
out.flush(); var spaces = " ".getBytes();
out.getFD().sync(); for(int i=0;i<1000; i++) fOut.write(spaces);
out.close(); fOut.flush();
fOut.getFD().sync();
fOut.close();
} catch (Throwable ignored) { } catch (Throwable ignored) {
} }
return ok; return ok;
@ -547,7 +648,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
state = 5; state = 5;
Util.println("Init Sync"); Util.println("Init Sync");
var ms = System.currentTimeMillis(); var ms = System.currentTimeMillis();
if(demand==0 || progView==null) syncProg((ms+999)/1000*1000, 0); if(demand==0 || progView==null) syncProg(ms, 0);
else { else {
avas.clear(); avas.clear();
var page = progView.pages.get(demand-1); 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) { public void afterCheck(Prog view, byte[] json) {
try { try {
var ms = (System.currentTimeMillis()+999)/1000*1000; var ms = System.currentTimeMillis();
if(view.isInsert) { if(view.isInsert) {
if(insView!=null) insView.release(); if(insView!=null) insView.release();
insView = view; insView = view;
try { try {
var page = avas.get(curAva); var page = avas.get(curAva);
for(var layer : page.layers) for(var src : layer.srcs) if(src.isShow) { 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; src.isShow = false;
} }
} catch (Throwable ignored) {} } 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")); var fOut = new FileOutputStream(Util.programDir + (view.isInsert?"/insert":"/program"));
fOut.write(json); fOut.write(json);
var spaces = " ".getBytes();
for(int i=0;i<1000; i++) fOut.write(spaces);
fOut.flush(); fOut.flush();
fOut.getFD().sync(); fOut.getFD().sync();
fOut.close(); fOut.close();
@ -671,6 +774,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
} }
} }
public LinkedList<ShowHide> showHides = new LinkedList<>(); public LinkedList<ShowHide> showHides = new LinkedList<>();
public HashSet<Prog.Source> showeds = new HashSet<>();
boolean contains(Prog.Source src, char act) { boolean contains(Prog.Source src, char act) {
for(var showHide : showHides) if(showHide.src==src && showHide.act==act) return true; for(var showHide : showHides) if(showHide.src==src && showHide.act==act) return true;
return false; return false;
@ -683,17 +788,28 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
while(iter.hasNext()) { while(iter.hasNext()) {
var showHide = iter.next(); var showHide = iter.next();
if(showHide.time > milli) { if(showHide.time > milli) {
if(showHide.time > milli+250) break; if(showHide.time > milli+400) break;
else continue; else continue;
} }
if(showHide.act=='H') { if(showHide.act=='H') {
showHide.src.hide(); showHide.src.hide();
showeds.remove(showHide.src);
if(! contains(showHide.src, 'S')) showHide.src.release(); if(! contains(showHide.src, 'S')) showHide.src.release();
iter.remove(); iter.remove();
} else if(showHide.act=='S') { } else if(showHide.act=='S') {
showHide.src.show(); showHide.src.show();
showeds.add(showHide.src);
iter.remove(); 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; boolean noProg = progView == null && insView==null;
if(noProg && showHides.isEmpty()) { if(noProg && showHides.isEmpty()) {
@ -717,7 +833,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli, showHides); if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli, showHides);
else { else {
for(var layer : lastPage.layers) for(var src : layer.srcs) if(src.isShow) { 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; src.isShow = false;
} }
if(isInsert) { 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(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) { for(var layer : insView.pages.get(i).layers) for(var src : layer.srcs) {
var iii = insView; var iii = insView;
showHides.add(new ShowHide(milli + 1000, ()->{ showHides.add(new ShowHide(milli + 500, ()->{
iii.removeView(src.view); iii.removeView(src.view);
if(src.view instanceof Choreographer.FrameCallback) iii.calls.remove((Choreographer.FrameCallback) 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()) { if(insView.pages.isEmpty()) {
var iii = insView; var iii = insView;
showHides.add(new ShowHide(milli + 1000, iii::release)); showHides.add(new ShowHide(milli + 500, iii::release));
insView = null; insView = null;
try { try {
var fOut = new FileOutputStream(Util.programDir + "/insert"); var fOut = new FileOutputStream(Util.programDir + "/insert");
@ -820,7 +936,6 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
start += avas.get(curAva++).tDur; start += avas.get(curAva++).tDur;
} while(curAva < avas.size() && start<=milli); } while(curAva < avas.size() && start<=milli);
start -= avas.get(--curAva).tDur; start -= avas.get(--curAva).tDur;
syncMs = milli;
Util.println("Sync. dur: "+dur+" milli: "+milli+" start: "+start+" diff: "+(milli - start)); Util.println("Sync. dur: "+dur+" milli: "+milli+" start: "+start+" diff: "+(milli - start));
} }
avas.get(curAva).setMillis(start, milli, showHides); avas.get(curAva).setMillis(start, milli, showHides);
@ -832,7 +947,11 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
} }
waitTo = milli + 1000; waitTo = milli + 1000;
if(state!=2) { if(state!=2) {
setContentView(backView); if(backViewL==null) setContentView(backView);
else {
setContentView(backViewL);
backViewL.loadUrl("file:///android_asset/local_page.html");
}
state = 2; state = 2;
} }
Util.println("No Avas, back"); Util.println("No Avas, back");

View File

@ -87,6 +87,8 @@ public class MainService extends Service {
else { else {
var fOut = new FileOutputStream(Util.programDir + "/program"); var fOut = new FileOutputStream(Util.programDir + "/program");
fOut.write(json); fOut.write(json);
var spaces = " ".getBytes();
for(int i=0;i<1000; i++) fOut.write(spaces);
fOut.flush(); fOut.flush();
fOut.getFD().sync(); fOut.getFD().sync();
fOut.close(); fOut.close();

View File

@ -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;
}
}
}

View File

@ -99,6 +99,7 @@ public class Prog extends AbsLayout {
page.repeatTimes = pageMap.intg("repeatTimes", 1); page.repeatTimes = pageMap.intg("repeatTimes", 1);
page.parse(pageMap.jslist("schedules")); page.parse(pageMap.jslist("schedules"));
var waitAudio = pageMap.bool("waitAudio"); var waitAudio = pageMap.bool("waitAudio");
var zzz = 0;
HashMap<String, Prog.Source> videoMap = new HashMap<>(); HashMap<String, Prog.Source> videoMap = new HashMap<>();
for(int ll=layers.size()-1; ll>=0; ll--) { for(int ll=layers.size()-1; ll>=0; ll--) {
var layer = new Layer(); var layer = new Layer();
@ -112,6 +113,7 @@ public class Prog extends AbsLayout {
bdWidth = bdEle.img.getHeight(); bdWidth = bdEle.img.getHeight();
} }
var src = new Source(); var src = new Source();
src.z = zzz++;
for(var source : sources) { for(var source : sources) {
src.type = source.stnn("_type"); src.type = source.stnn("_type");
if(src.type.isEmpty()) continue; if(src.type.isEmpty()) continue;
@ -271,10 +273,11 @@ public class Prog extends AbsLayout {
src.box = source.bool("isPreSplit") ? this : box; src.box = source.bool("isPreSplit") ? this : box;
layer.srcs.add(src); layer.srcs.add(src);
src = new Source(); src = new Source();
src.z = zzz++;
} }
} else if(src.type.equals("Audio")) { } else if(src.type.equals("Audio")) {
if(id==null) continue; if(id==null) continue;
src.typ = 'A'; src.typ = 'V';
src.path = Util.programDir + "/" +id; src.path = Util.programDir + "/" +id;
src.vol = source.intg("vol", 100) / 100.0f; src.vol = source.intg("vol", 100) / 100.0f;
src.dur = dur; src.dur = dur;
@ -282,6 +285,7 @@ public class Prog extends AbsLayout {
src.box = box; src.box = box;
layer.srcs.add(src); layer.srcs.add(src);
src = new Source(); src = new Source();
src.z = zzz++;
} else if(src.type.equals("Scroll")) { } else if(src.type.equals("Scroll")) {
JSList<String> imgs = source.jslist("imgs"); JSList<String> imgs = source.jslist("imgs");
if(imgs.isEmpty()) continue; if(imgs.isEmpty()) continue;
@ -365,9 +369,11 @@ public class Prog extends AbsLayout {
src.view = imgView; src.view = imgView;
src.view.setVisibility(GONE); src.view.setVisibility(GONE);
src.view.setLayoutParams(geo); src.view.setLayoutParams(geo);
src.view.setZ(src.z);
box.addView(src.view); box.addView(src.view);
layer.srcs.add(src); layer.srcs.add(src);
src = new Source(); src = new Source();
src.z = zzz++;
} }
} }
} else if(src.type.equals("DigitalClock")) src.view = new SrcDigitalClock(this, source); } 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; if(src.view==null) continue;
src.view.setVisibility(GONE); src.view.setVisibility(GONE);
src.view.setLayoutParams(geo); src.view.setLayoutParams(geo);
src.view.setZ(src.z);
(source.bool("isPreSplit") ? this : box).addView(src.view); (source.bool("isPreSplit") ? this : box).addView(src.view);
layer.srcs.add(src); layer.srcs.add(src);
src = new Source(); src = new Source();
src.z = zzz++;
} }
if(bdEle!=null && bdStart < bdEnd) { if(bdEle!=null && bdStart < bdEnd) {
JSList<Long> geometry = border.jslist("geometry"); JSList<Long> geometry = border.jslist("geometry");
@ -449,6 +457,7 @@ public class Prog extends AbsLayout {
src.rotate = (float) border.dbl("rotate"); src.rotate = (float) border.dbl("rotate");
src.view = bdEle; src.view = bdEle;
src.view.setVisibility(GONE); 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())); 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); layer.srcs.add(src);
} }
@ -516,7 +525,7 @@ public class Prog extends AbsLayout {
AbsLayout box; AbsLayout box;
String path; String path;
float vol; float vol;
int dur; int dur, z;
boolean useSW; boolean useSW;
public Source() { public Source() {
@ -528,7 +537,8 @@ public class Prog extends AbsLayout {
void show() { void show() {
if(typ=='V') { if(typ=='V') {
if(alphaShow==1) return; if(alphaShow==1) return;
((SrcVideo)view).ijkPlayer.start(); var video = (SrcVideo) view;
video.ijkPlayer.start();
view.setAlpha((alphaShow = 1) * alpha); view.setAlpha((alphaShow = 1) * alpha);
} else { } else {
if(view.getVisibility()==VISIBLE) return; 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(isEntryRand) entryEff = Effect.values()[Util.rand.nextInt(Effect.values().length)];
if(isExitRand) exitEff = Effect.values()[Util.rand.nextInt(Effect.values().length)]; if(isExitRand) exitEff = Effect.values()[Util.rand.nextInt(Effect.values().length)];
resetEff(); resetEff();
doEff();
if(tts!=null) tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, null); if(tts!=null) tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, null);
} }
void hide() { void hide() {
@ -554,17 +563,24 @@ public class Prog extends AbsLayout {
if(view instanceof GifImageView) ((GifImageView) view).setImageURI(uri); if(view instanceof GifImageView) ((GifImageView) view).setImageURI(uri);
if(tts!=null && tts.isSpeaking()) tts.stop(); if(tts!=null && tts.isSpeaking()) tts.stop();
} }
void prepare(long seek) { void prepare(List<MainActivity.ShowHide> shows, long seek) {
var ms = System.currentTimeMillis();
if(view==null) { if(view==null) {
var video = new SrcVideo(box.getContext(), path, vol, dur, seek, useSW); var video = new SrcVideo(box.getContext(), path, vol, dur, seek, useSW);
video.setLayoutParams(geo); video.setLayoutParams(geo);
video.setAlpha(alphaShow = 0); video.setAlpha(alphaShow = 0);
video.setVisibility(VISIBLE); video.setVisibility(VISIBLE);
video.setZ(z);
box.addView(view = video); 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; shows.add(new MainActivity.ShowHide(startMilli+500, this, 'S'));
Util.println(" prepare ms "+ms); isShow = true;
} }
void release() { void release() {
if(view instanceof SrcVideo) { if(view instanceof SrcVideo) {
@ -574,6 +590,7 @@ public class Prog extends AbsLayout {
} }
} }
void doEff() { void doEff() {
if(view==null) return;
var w = view.getLayoutParams().width; var w = view.getLayoutParams().width;
var h = view.getLayoutParams().height; var h = view.getLayoutParams().height;
if(ff < entryDur) { if(ff < entryDur) {
@ -754,11 +771,7 @@ public class Prog extends AbsLayout {
for(var src : layer.srcs) { for(var src : layer.srcs) {
src.endMilli = start + src.endTime; src.endMilli = start + src.endTime;
src.startMilli = start + src.startTime; src.startMilli = start + src.startTime;
if(src.startTime == 0) { if(src.startTime == 0) src.prepare(shows, cur - src.startMilli);
src.prepare(cur - src.startMilli);
shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S'));
src.isShow = true;
}
} }
} }
if(Util.logOn) { if(Util.logOn) {
@ -778,14 +791,11 @@ public class Prog extends AbsLayout {
for(var src : layer.srcs) { for(var src : layer.srcs) {
if(src.isShow) { if(src.isShow) {
if(milli >= src.endMilli) { 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; src.isShow = false;
} }
else src.doEff();
} else if(milli < src.endMilli && milli >= src.startMilli) { } else if(milli < src.endMilli && milli >= src.startMilli) {
src.prepare(milli - src.startMilli); src.prepare(shows, milli - src.startMilli);
shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S'));
src.isShow = true;
} }
} }
if(milli >= layer.endMilli) { if(milli >= layer.endMilli) {
@ -793,14 +803,7 @@ public class Prog extends AbsLayout {
for(var src : layer.srcs) { for(var src : layer.srcs) {
src.endMilli += layer.dur; src.endMilli += layer.dur;
src.startMilli += layer.dur; src.startMilli += layer.dur;
if(src.startTime > 0) { if(src.startTime == 0) src.prepare(shows, 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;
}
} }
} }
} }

View File

@ -23,7 +23,6 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL
super(context); super(context);
this.vol = vol; this.vol = vol;
setSurfaceTextureListener(this); setSurfaceTextureListener(this);
Util.println(" video new");
ijkPlayer = new IjkMediaPlayer(); ijkPlayer = new IjkMediaPlayer();
if(! useSW) { if(! useSW) {
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1); ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1);
@ -41,7 +40,10 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL
ijkPlayer.setOnPreparedListener(null); ijkPlayer.setOnPreparedListener(null);
if(getAlpha() < 0.25) { if(getAlpha() < 0.25) {
ijkPlayer.pause(); 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); } else if(seek>=1000) ijkPlayer.seekTo(seek);
bitRate = ijkPlayer.getBitRate(); bitRate = ijkPlayer.getBitRate();
var diff = dur - ijkPlayer.getDuration(); var diff = dur - ijkPlayer.getDuration();
@ -77,26 +79,13 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {} public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {}
void release() { void release() {
Util.println(" video releasing");
if(ijkPlayer!=null) { if(ijkPlayer!=null) {
ijkPlayer.release(); ijkPlayer.release();
ijkPlayer = null; ijkPlayer = null;
} }
Util.println(" surface releasing");
if(surface!=null) { if(surface!=null) {
surface.release(); surface.release();
surface = null; 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);
// }
// }
} }

View File

@ -66,7 +66,7 @@ public class SrcWeb extends WebView implements Choreographer.FrameCallback {
}); });
loadUrl(url); loadUrl(url);
refresh = json.intg("refreshSec")*1000; 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); prog.calls.add(this);
nextMs = System.currentTimeMillis() + refresh; nextMs = System.currentTimeMillis() + refresh;
} }
@ -111,8 +111,10 @@ public class SrcWeb extends WebView implements Choreographer.FrameCallback {
loadUrl(url); loadUrl(url);
Util.println(" WebView Refreshed"); Util.println(" WebView Refreshed");
} else if(calls!=null) { } else if(calls!=null) {
calls.remove(this); var calls = this.calls;
calls = null; this.calls = null;
var acti = MainActivity.ins;
if(acti!=null) acti.showHides.add(new MainActivity.ShowHide(0, () -> calls.remove(this)));
} }
} }
} }

View File

@ -141,6 +141,8 @@ public class TCPThread extends Thread {
else { else {
var fOut = new FileOutputStream(Util.programDir + "/program"); var fOut = new FileOutputStream(Util.programDir + "/program");
fOut.write(json); fOut.write(json);
var spaces = " ".getBytes();
for(int i=0;i<1000; i++) fOut.write(spaces);
fOut.flush(); fOut.flush();
fOut.getFD().sync(); fOut.getFD().sync();
fOut.close(); fOut.close();
@ -191,6 +193,8 @@ public class TCPThread extends Thread {
else { else {
var fOut = new FileOutputStream(Util.programDir + "/program"); var fOut = new FileOutputStream(Util.programDir + "/program");
fOut.write(json); fOut.write(json);
fOut.write("{}".getBytes());
var spaces = " ".getBytes();
fOut.flush(); fOut.flush();
fOut.getFD().sync(); fOut.getFD().sync();
fOut.close(); 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(" 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("\n");
writer.append(" Launch: ").append(fmt.format(main.launchMilli)).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.insView==null) writer.append(" InseView is Null\n");
if(main.progView==null) writer.append(" ProgView is Null\n"); if(main.progView==null) writer.append(" ProgView is Null\n");
if(main.avas.isEmpty()) writer.append(" No Avas\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("\n\n");
writer.append(Util.cfg.toStr()); writer.append(Util.cfg.toStr());
writer.append("\n\n"); writer.append("\n\n");
writer.append("showeds "+main.showeds.size()).append(" showHides "+main.showHides.size());
writer.append("\n\n");
var latch = new CountDownLatch(1); var latch = new CountDownLatch(1);
if(main!=null) main.runOnUiThread(() -> { if(main!=null) main.runOnUiThread(() -> {
@ -376,7 +381,7 @@ public class TCPThread extends Thread {
var inse = new File(Util.programDir+"/insert"); var inse = new File(Util.programDir+"/insert");
var files = new File(Util.programDir).listFiles(); var files = new File(Util.programDir).listFiles();
if(inse.isFile()) { 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()))+"*/"); 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); var writer = new OutputStreamWriter(out);
writer.append("insert:\n"); writer.append("insert:\n");
@ -387,7 +392,7 @@ public class TCPThread extends Thread {
var prog = new File(Util.programDir+"/program"); var prog = new File(Util.programDir+"/program");
if(! prog.isFile()) new JSMap("msg", "'program' file not exist").write(out); if(! prog.isFile()) new JSMap("msg", "'program' file not exist").write(out);
else { 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()))+"*/"); 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); var writer = new OutputStreamWriter(out);
writer.append("\nprogram:\n"); writer.append("\nprogram:\n");

View File

@ -43,6 +43,7 @@ public class Util {
public static volatile long downId; public static volatile long downId;
public static int screenWidth = 1920, screenHeight = 1080; public static int screenWidth = 1920, screenHeight = 1080;
public static double lat, lng; public static double lat, lng;
public static char cardType = 0;
public static boolean isScreenOn, isAudioGain, logOn; public static boolean isScreenOn, isAudioGain, logOn;
public static void initDir(Context ctx) { public static void initDir(Context ctx) {