feat: 完成多项功能迭代与项目配置优化

1. 新增WiFi连接UI预制体与多语言文案
2. 重构BLE通信通知处理逻辑
3. 调整数据库总工作时长类型为float
4. 更新项目与Android构建配置
5. 主页添加消杀数据上传进度提示
This commit is contained in:
“虞渠成” 2026-07-06 15:40:04 +08:00
parent a7024a6e19
commit 1847d74de2
10 changed files with 85 additions and 32 deletions

View File

@ -6,18 +6,21 @@ using System.IO;
public class iOSWiFiBuildPostProcessor public class iOSWiFiBuildPostProcessor
{ {
private const string WIFI_INFO_ENTITLEMENT = "com.apple.developer.networking.wifi-info";
[PostProcessBuild] [PostProcessBuild]
public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject) public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
{ {
if (target != BuildTarget.iOS) return; if (target != BuildTarget.iOS) return;
// ===== 1. 配置 Info.plist =====
string plistPath = Path.Combine(pathToBuiltProject, "Info.plist"); string plistPath = Path.Combine(pathToBuiltProject, "Info.plist");
PlistDocument plist = new PlistDocument(); PlistDocument plist = new PlistDocument();
plist.ReadFromString(File.ReadAllText(plistPath)); plist.ReadFromString(File.ReadAllText(plistPath));
PlistElementDict rootDict = plist.root; PlistElementDict rootDict = plist.root;
// 添加位置权限描述iOS 获取 WiFi 需要) // 添加位置权限描述iOS 13+ 获取 WiFi 需要)
rootDict.SetString("NSLocationWhenInUseUsageDescription", "需要位置权限来获取当前 WiFi 信息"); rootDict.SetString("NSLocationWhenInUseUsageDescription", "需要位置权限来获取当前 WiFi 信息");
// 添加蓝牙权限描述 // 添加蓝牙权限描述
@ -30,15 +33,44 @@ public class iOSWiFiBuildPostProcessor
bgModes.AddString("bluetooth-central"); bgModes.AddString("bluetooth-central");
File.WriteAllText(plistPath, plist.WriteToString()); File.WriteAllText(plistPath, plist.WriteToString());
// 添加 CoreBluetooth.framework // ===== 2. 配置 Xcode 项目 =====
string projPath = PBXProject.GetPBXProjectPath(pathToBuiltProject); string projPath = PBXProject.GetPBXProjectPath(pathToBuiltProject);
PBXProject proj = new PBXProject(); PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath)); proj.ReadFromString(File.ReadAllText(projPath));
string targetGuid = proj.GetUnityMainTargetGuid(); string targetGuid = proj.GetUnityMainTargetGuid();
proj.AddFrameworkToProject(targetGuid, "CoreBluetooth.framework", false); proj.AddFrameworkToProject(targetGuid, "CoreBluetooth.framework", false);
// ===== 3. 添加 Access WiFi Information 权限iOS 14+ 必需)=====
string entitlementsFileName = "app.entitlements";
string entitlementsPath = Path.Combine(pathToBuiltProject, entitlementsFileName);
// 创建或读取 entitlements 文件
PlistDocument entitlements = new PlistDocument();
if (File.Exists(entitlementsPath))
{
entitlements.ReadFromString(File.ReadAllText(entitlementsPath));
}
else
{
entitlements.root.SetString("aps-environment", "development");
}
// 添加 WiFi 信息访问权限iOS 14+
entitlements.root.SetBoolean(WIFI_INFO_ENTITLEMENT, true);
File.WriteAllText(entitlementsPath, entitlements.WriteToString());
// 设置 entitlements 文件到 Xcode 项目
proj.SetBuildProperty(targetGuid, "CODE_SIGN_ENTITLEMENTS", entitlementsFileName);
// 同时设置 framework target
string frameworkTargetGuid = proj.GetUnityFrameworkTargetGuid();
if (!string.IsNullOrEmpty(frameworkTargetGuid))
{
proj.SetBuildProperty(frameworkTargetGuid, "CODE_SIGN_ENTITLEMENTS", entitlementsFileName);
}
File.WriteAllText(projPath, proj.WriteToString()); File.WriteAllText(projPath, proj.WriteToString());
} }
} }

View File

@ -4,16 +4,16 @@ apply plugin: 'com.android.library'
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
// Android Resolver Dependencies Start // Android Resolver Dependencies Start
implementation 'com.google.android.gms:play-services-base:18.10.0' // Assets/Firebase/Editor/DatabaseDependencies.xml:20 implementation 'com.google.android.gms:play-services-base:18.10.0' // Assets/Firebase/Editor/DatabaseDependencies.xml:17
// implementation 'com.google.firebase:firebase-analytics:23.0.0' // Assets/Firebase/Editor/AnalyticsDependencies.xml:13 // implementation 'com.google.firebase:firebase-analytics:23.0.0' // Assets/Firebase/Editor/AnalyticsDependencies.xml:11
implementation 'com.google.firebase:firebase-analytics:23.2.0' // Assets/Firebase/Editor/DatabaseDependencies.xml:18 implementation 'com.google.firebase:firebase-analytics:23.2.0' // Assets/Firebase/Editor/DatabaseDependencies.xml:15
implementation 'com.google.firebase:firebase-analytics-unity:13.5.0' // Assets/Firebase/Editor/AnalyticsDependencies.xml:18 implementation 'com.google.firebase:firebase-analytics-unity:13.5.0' // Assets/Firebase/Editor/AnalyticsDependencies.xml:16
implementation 'com.google.firebase:firebase-app-unity:13.12.0' // Assets/Firebase/Editor/AppDependencies.xml:25 implementation 'com.google.firebase:firebase-app-unity:13.12.0' // Assets/Firebase/Editor/AppDependencies.xml:22
implementation 'com.google.firebase:firebase-auth:24.1.0' // Assets/Firebase/Editor/AuthDependencies.xml:16 implementation 'com.google.firebase:firebase-auth:24.1.0' // Assets/Firebase/Editor/AuthDependencies.xml:13
implementation 'com.google.firebase:firebase-auth-unity:13.12.0' // Assets/Firebase/Editor/AuthDependencies.xml:23 implementation 'com.google.firebase:firebase-auth-unity:13.12.0' // Assets/Firebase/Editor/AuthDependencies.xml:20
implementation 'com.google.firebase:firebase-common:22.0.1' // Assets/Firebase/Editor/AppDependencies.xml:16 implementation 'com.google.firebase:firebase-common:22.0.1' // Assets/Firebase/Editor/AppDependencies.xml:13
implementation 'com.google.firebase:firebase-database:22.0.1' // Assets/Firebase/Editor/DatabaseDependencies.xml:16 implementation 'com.google.firebase:firebase-database:22.0.1' // Assets/Firebase/Editor/DatabaseDependencies.xml:13
implementation 'com.google.firebase:firebase-database-unity:13.12.0' // Assets/Firebase/Editor/DatabaseDependencies.xml:25 implementation 'com.google.firebase:firebase-database-unity:13.12.0' // Assets/Firebase/Editor/DatabaseDependencies.xml:22
// Android Resolver Dependencies End // Android Resolver Dependencies End
**DEPS**} **DEPS**}

View File

@ -19,7 +19,7 @@ dependencyResolutionManagement {
// Android Resolver Repos Start // Android Resolver Repos Start
def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/") def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
maven { maven {
url (unityProjectPath + "/Assets/GeneratedLocalRepo/Firebase/m2repository") // Assets/Firebase/Editor/AnalyticsDependencies.xml:18, Assets/Firebase/Editor/AppDependencies.xml:25, Assets/Firebase/Editor/AuthDependencies.xml:23, Assets/Firebase/Editor/DatabaseDependencies.xml:25 url (unityProjectPath + "/Assets/GeneratedLocalRepo/Firebase/m2repository") // Assets/Firebase/Editor/AnalyticsDependencies.xml:16, Assets/Firebase/Editor/AppDependencies.xml:22, Assets/Firebase/Editor/AuthDependencies.xml:20, Assets/Firebase/Editor/DatabaseDependencies.xml:22
} }
mavenLocal() mavenLocal()
// Android Resolver Repos End // Android Resolver Repos End

View File

@ -1508,6 +1508,11 @@
"key": "100302", "key": "100302",
"zh": "恢复出厂设置失败,请稍后重试", "zh": "恢复出厂设置失败,请稍后重试",
"en": "Reset failed, please try again later" "en": "Reset failed, please try again later"
},
{
"key": "100303",
"zh": "获取到{0}条消杀数据,正在上传,请稍后",
"en": "Uploading {0} elimination records, please wait..."
} }

View File

@ -50,7 +50,6 @@ namespace Kill.Bluetooth
public event Action<StatisticsData> OnStatisticsDataReceived; // 收到统计数据 public event Action<StatisticsData> OnStatisticsDataReceived; // 收到统计数据
public event Action<SensorData> OnSensorDataReceived; // 收到传感器数据 public event Action<SensorData> OnSensorDataReceived; // 收到传感器数据
public event Action<MosquitoData> OnMosquitoDataReceived; // 收到蚊虫数据通知 public event Action<MosquitoData> OnMosquitoDataReceived; // 收到蚊虫数据通知
public event Action<byte[]> OnDataReceived; // 收到原始数据
public event Action<string> OnRawDataReceived; // 收到原始数据(十六进制字符串) public event Action<string> OnRawDataReceived; // 收到原始数据(十六进制字符串)
public event Action<string> OnRawDataSent; // 发送原始数据(十六进制字符串) public event Action<string> OnRawDataSent; // 发送原始数据(十六进制字符串)
public event Action<string> OnCommunicationError; // 通信错误 public event Action<string> OnCommunicationError; // 通信错误
@ -1954,7 +1953,6 @@ namespace Kill.Bluetooth
if (success) if (success)
{ {
mosquitoDataCount=(int)count; mosquitoDataCount=(int)count;
OnDataReceived+=HandleMosquitoDataNotification;
Log($"蚊虫数据请求成功,期望接收 {count} 条数据"); Log($"蚊虫数据请求成功,期望接收 {count} 条数据");
} }
@ -1965,15 +1963,12 @@ namespace Kill.Bluetooth
} }
public int mosquitoDataCount=0; public int mosquitoDataCount=0;
/// <summary> /// <summary>
/// 处理蚊虫数据通知 (通知类型 0x0B) /// 处理蚊虫数据通知
/// </summary> /// </summary>
public void HandleMosquitoDataNotification(byte[] notifyData) private void HandleMosquitoDataNotification(byte[] notifyData)
{ {
if(mosquitoDataCount<=0) if(mosquitoDataCount<=0)
{
OnDataReceived-=HandleMosquitoDataNotification;
return; return;
}
if (notifyData == null || notifyData.Length < 20) if (notifyData == null || notifyData.Length < 20)
{ {
LogError("蚊虫数据通知格式错误数据长度不足20字节"); LogError("蚊虫数据通知格式错误数据长度不足20字节");
@ -2421,10 +2416,15 @@ namespace Kill.Bluetooth
private void HandleReceivedFrame(BLEFrame frame) private void HandleReceivedFrame(BLEFrame frame)
{ {
bool wasWaitingResponse = IsWaitingResponse; bool wasWaitingResponse = IsWaitingResponse;
// 检查是否是对等待命令的响应 // 优先处理设备主动通知,避免被等待响应的流程拦截
if (IsWaitingResponse && frame.Command == LastCommand) if (frame.ReadWrite == BLEConstants.RW_NOTIFY)
{ {
HandleNotification(frame.Command, frame.Data);
}
else if (IsWaitingResponse && frame.Command == LastCommand)
{
// 匹配到等待的响应
IsWaitingResponse = false; IsWaitingResponse = false;
_responseTimer = 0; _responseTimer = 0;
@ -2432,11 +2432,6 @@ namespace Kill.Bluetooth
_pendingCallback?.Invoke(response); _pendingCallback?.Invoke(response);
_pendingCallback = null; _pendingCallback = null;
} }
else if (frame.ReadWrite == BLEConstants.RW_NOTIFY)
{
// 处理设备主动通知
OnDataReceived?.Invoke(frame.Data);
}
else if (IsWaitingResponse) else if (IsWaitingResponse)
{ {
// 尝试处理非匹配的响应,可能是硬件响应延迟 // 尝试处理非匹配的响应,可能是硬件响应延迟
@ -2455,6 +2450,22 @@ namespace Kill.Bluetooth
} }
} }
/// <summary>
/// 通用的设备通知分发器,按命令类型路由到对应处理逻辑
/// </summary>
private void HandleNotification(byte command, byte[] data)
{
switch (command)
{
case BLEConstants.NOTIFY_MOSQUITO_DATA:
HandleMosquitoDataNotification(data);
break;
default:
Log($"未处理的设备通知 0x{command:X2}");
break;
}
}
/// <summary> /// <summary>
/// 响应超时处理 /// 响应超时处理
/// </summary> /// </summary>

View File

@ -270,7 +270,7 @@ namespace Kill.Managers
public string bind_time; public string bind_time;
public string device_model; public string device_model;
public string firmware_version; public string firmware_version;
public int? total_work_hours; public float? total_work_hours;
public int? total_mosquitoes_killed; public int? total_mosquitoes_killed;
public string location; public string location;
public string last_online_time; public string last_online_time;

View File

@ -1400,6 +1400,8 @@ namespace Kill.UI.Pages
Coroutine waitToPostMosquitoDatas=null; Coroutine waitToPostMosquitoDatas=null;
private void OnMosquitoDataReceived(MosquitoData mosquitoData) private void OnMosquitoDataReceived(MosquitoData mosquitoData)
{ {
if(mosquitoDatas==null)
return;
DateTime now=DateTime.Now; DateTime now=DateTime.Now;
mosquitoDatas.Add(mosquitoData); mosquitoDatas.Add(mosquitoData);
if(waitToPostMosquitoDatas!=null) if(waitToPostMosquitoDatas!=null)
@ -1469,6 +1471,9 @@ namespace Kill.UI.Pages
{ {
if (success) if (success)
{ {
string tip=LanguageManager.Instance.GetLanguage("100303");
tip=tip.Replace("{0}",count.ToString());
ToastUI.ShowText(tip);
Debug.Log($"已请求 {count.Count} 条蚊虫数据,等待接收..."); Debug.Log($"已请求 {count.Count} 条蚊虫数据,等待接收...");
mosquitoDatas=new List<MosquitoData>(); mosquitoDatas=new List<MosquitoData>();
aimMosquitoDataCount=(int)count.Count; aimMosquitoDataCount=(int)count.Count;

View File

@ -13,7 +13,7 @@ PlayerSettings:
useOnDemandResources: 0 useOnDemandResources: 0
accelerometerFrequency: 100 accelerometerFrequency: 100
companyName: photonmatrix companyName: photonmatrix
productName: photonmatrix productName: Photon Matrix
defaultCursor: {fileID: 0} defaultCursor: {fileID: 0}
cursorHotspot: {x: 0, y: 0} cursorHotspot: {x: 0, y: 0}
m_SplashScreenBackgroundColor: {r: 0.08627451, g: 0.08627451, b: 0.08627451, a: 1} m_SplashScreenBackgroundColor: {r: 0.08627451, g: 0.08627451, b: 0.08627451, a: 1}
@ -176,7 +176,7 @@ PlayerSettings:
VisionOS: 0 VisionOS: 0
iPhone: 0 iPhone: 0
tvOS: 0 tvOS: 0
overrideDefaultApplicationIdentifier: 0 overrideDefaultApplicationIdentifier: 1
AndroidBundleVersionCode: 1 AndroidBundleVersionCode: 1
AndroidMinSdkVersion: 33 AndroidMinSdkVersion: 33
AndroidTargetSdkVersion: 0 AndroidTargetSdkVersion: 0