add: fuck you android

This commit is contained in:
2025-08-09 20:35:05 +08:00
parent 5e3ef9be21
commit c42562bc15
10 changed files with 1371 additions and 1 deletions

View File

@ -6,13 +6,33 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- Android 12+ 新蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!-- 位置权限(蓝牙扫描需要) -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- 保活相关权限 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<uses-permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
<!-- 声明不需要硬件蓝牙支持(可选) -->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />
</manifest>

View File

@ -0,0 +1,129 @@
using Android.App;
using Android.App.Job;
using Android.Content;
using Android.OS;
namespace HeartRateMonitorAndroid.Platforms.Android
{
[Service(Name = "com.nuanrmxi.heartratemonitor.HeartRateJobService",
Permission = "android.permission.BIND_JOB_SERVICE",
Enabled = true,
Exported = false)]
public class HeartRateJobService : JobService
{
private const int JOB_ID = 1002;
public override bool OnStartJob(JobParameters @params)
{
System.Diagnostics.Debug.WriteLine("HeartRateJobService: OnStartJob called");
// 在后台线程中执行任务
Task.Run(() =>
{
try
{
CheckAndRestartService();
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"HeartRateJobService Error: {ex.Message}");
}
finally
{
// 完成任务
JobFinished(@params, false);
}
});
return true; // 返回true表示任务在后台执行
}
public override bool OnStopJob(JobParameters @params)
{
System.Diagnostics.Debug.WriteLine("HeartRateJobService: OnStopJob called");
return false; // 返回false表示不需要重新调度
}
private void CheckAndRestartService()
{
try
{
// 检查前台服务是否正在运行
if (!IsServiceRunning(typeof(HeartRateKeepAliveService)))
{
System.Diagnostics.Debug.WriteLine("HeartRateJobService: 前台服务未运行,正在重启...");
var intent = new Intent(this, typeof(HeartRateKeepAliveService));
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
StartForegroundService(intent);
}
else
{
StartService(intent);
}
}
else
{
System.Diagnostics.Debug.WriteLine("HeartRateJobService: 前台服务正常运行");
}
// 重新调度下一次检查
ScheduleNextJob();
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"CheckAndRestartService Error: {ex.Message}");
}
}
private bool IsServiceRunning(System.Type serviceType)
{
try
{
var activityManager = GetSystemService(ActivityService) as ActivityManager;
var services = activityManager?.GetRunningServices(int.MaxValue);
if (services != null)
{
foreach (var service in services)
{
if (service.Service.ClassName.Equals(serviceType.FullName))
{
return true;
}
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"IsServiceRunning Error: {ex.Message}");
}
return false;
}
private void ScheduleNextJob()
{
try
{
var jobScheduler = GetSystemService(JobSchedulerService) as JobScheduler;
var jobInfo = new JobInfo.Builder(JOB_ID, new ComponentName(this, Java.Lang.Class.FromType(typeof(HeartRateJobService))))
.SetRequiredNetworkType(NetworkType.Any)
.SetPersisted(true)
.SetMinimumLatency(10 * 60 * 1000) // 最少10分钟后执行
.SetOverrideDeadline(15 * 60 * 1000) // 最多15分钟后必须执行
.SetRequiresCharging(false)
.SetRequiresDeviceIdle(false)
.Build();
var result = jobScheduler?.Schedule(jobInfo);
System.Diagnostics.Debug.WriteLine($"HeartRateJobService: 下次任务调度结果: {result}");
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"ScheduleNextJob Error: {ex.Message}");
}
}
}
}

View File

@ -0,0 +1,469 @@
using Android.App;
using Android.Content;
using Android.OS;
using AndroidX.Core.App;
using Android.Content.PM;
using HeartRateMonitorAndroid.Services;
using Microsoft.Extensions.DependencyInjection;
using Java.Lang;
using Android.Provider;
using Android.App.Job;
using Resource = Android.Resource;
namespace HeartRateMonitorAndroid.Platforms.Android
{
[Service(Name = "com.nuanrmxi.heartratemonitor.HeartRateKeepAliveService",
Enabled = true,
Exported = false,
ForegroundServiceType = ForegroundService.TypeDataSync | ForegroundService.TypeLocation)]
public class HeartRateKeepAliveService : Service
{
private const int NOTIFICATION_ID = 1001;
private const string CHANNEL_ID = "HeartRateMonitorChannel";
private const string CHANNEL_NAME = "心率监测服务";
private PowerManager.WakeLock _wakeLock;
private System.Timers.Timer _heartRateTimer;
private WebSocketService.HeartRateWebSocketClient _webSocketClient;
private BluetoothService _bluetoothService;
private bool _isServiceRunning = false;
public override void OnCreate()
{
base.OnCreate();
CreateNotificationChannel();
AcquireWakeLock();
InitializeServices();
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
if (!_isServiceRunning)
{
StartForegroundService();
StartHeartRateMonitoring();
ScheduleJobService();
_isServiceRunning = true;
}
// 返回START_STICKY确保服务被系统杀死后会重启
return StartCommandResult.Sticky;
}
public override IBinder OnBind(Intent intent)
{
return null;
}
private void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationImportance.Low)
{
Description = "心率监测后台服务通知"
};
channel.SetShowBadge(false);
channel.EnableLights(false);
channel.EnableVibration(false);
var notificationManager = GetSystemService(NotificationService) as NotificationManager;
notificationManager?.CreateNotificationChannel(channel);
}
}
private void StartForegroundService()
{
var intent = new Intent(this, typeof(MainActivity));
intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent,
PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.SetContentTitle("心率监测服务")
.SetContentText("正在后台监测心率数据")
.SetSmallIcon(Resource.Drawable.abc_dialog_material_background) // 使用系统图标
.SetContentIntent(pendingIntent)
.SetOngoing(true)
.SetPriority(NotificationCompat.PriorityLow)
.SetCategory(NotificationCompat.CategoryService)
.Build();
StartForeground(NOTIFICATION_ID, notification);
}
private void AcquireWakeLock()
{
var powerManager = GetSystemService(PowerService) as PowerManager;
_wakeLock = powerManager?.NewWakeLock(WakeLockFlags.Partial, "HeartRateMonitor::KeepAlive");
_wakeLock?.Acquire();
}
private void InitializeServices()
{
try
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 开始初始化服务");
// 获取服务实例
var serviceProvider = MauiApplication.Current?.Services;
_bluetoothService = serviceProvider?.GetService<BluetoothService>();
if (_bluetoothService == null)
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 无法获取BluetoothService实例创建新实例");
_bluetoothService = new BluetoothService();
}
else
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 成功获取BluetoothService实例");
}
// 初始化WebSocket客户端
var serverUrl = GetServerUrl();
if (!string.IsNullOrEmpty(serverUrl))
{
_webSocketClient = new WebSocketService.HeartRateWebSocketClient(serverUrl);
System.Diagnostics.Debug.WriteLine($"KeepAliveService: WebSocket客户端已初始化服务器: {serverUrl}");
}
else
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 无法获取服务器URL");
}
// 如果蓝牙服务可用,尝试重新连接之前连接的设备
if (_bluetoothService != null)
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 启动蓝牙重连任务");
Task.Run(async () => await ReconnectBluetoothDevice());
}
else
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 蓝牙服务不可用,跳过蓝牙重连");
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"InitializeServices Error: {ex.Message}");
}
}
private async Task ReconnectBluetoothDevice()
{
try
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 尝试重新连接蓝牙设备");
// 检查蓝牙状态
var bluetoothState = _bluetoothService.CheckBluetoothState();
if (!bluetoothState.Contains("准备就绪"))
{
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 蓝牙状态不可用: {bluetoothState}");
return;
}
// 尝试获取上次连接的设备地址
var lastConnectedDeviceAddress = _bluetoothService.GetLastConnectedDeviceAddress();
if (!string.IsNullOrEmpty(lastConnectedDeviceAddress))
{
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 尝试连接上次的设备: {lastConnectedDeviceAddress}");
// 首先尝试直接连接到已知设备
var directConnectSuccess = await _bluetoothService.ConnectToDeviceByAddressAsync(lastConnectedDeviceAddress);
if (!directConnectSuccess)
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 直接连接失败,开始扫描寻找设备");
// 注册临时设备发现事件处理器
bool deviceFound = false;
Action<Plugin.BLE.Abstractions.Contracts.IDevice> tempDeviceHandler = (device) =>
{
if (device.Id.ToString() == lastConnectedDeviceAddress)
{
deviceFound = true;
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 在扫描中找到目标设备: {device.Name}");
Task.Run(async () => await _bluetoothService.ConnectToDeviceAsync(device));
}
};
_bluetoothService.DeviceDiscovered += tempDeviceHandler;
try
{
// 开始扫描
await _bluetoothService.StartScanAsync();
// 等待10秒寻找目标设备
for (int i = 0; i < 100 && !deviceFound; i++)
{
await Task.Delay(100);
}
// 停止扫描
await _bluetoothService.StopScanAsync();
if (deviceFound)
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 成功找到并连接目标设备");
}
else
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 未找到目标设备");
}
}
finally
{
_bluetoothService.DeviceDiscovered -= tempDeviceHandler;
}
}
else
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 直接连接成功");
}
}
else
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 没有找到上次连接的设备信息,开始新的扫描");
// 注册临时设备发现事件处理器
bool anyDeviceFound = false;
Action<Plugin.BLE.Abstractions.Contracts.IDevice> tempDeviceHandler = (device) =>
{
if (!anyDeviceFound)
{
anyDeviceFound = true;
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 发现心率设备: {device.Name}");
Task.Run(async () => await _bluetoothService.ConnectToDeviceAsync(device));
}
};
_bluetoothService.DeviceDiscovered += tempDeviceHandler;
try
{
// 如果没有上次连接的设备信息,开始扫描
await _bluetoothService.StartScanAsync();
// 扫描15秒后停止
await Task.Delay(15000);
await _bluetoothService.StopScanAsync();
if (anyDeviceFound)
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 找到并连接了新设备");
}
else
{
System.Diagnostics.Debug.WriteLine("KeepAliveService: 未找到任何心率设备");
}
}
finally
{
_bluetoothService.DeviceDiscovered -= tempDeviceHandler;
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"ReconnectBluetoothDevice Error: {ex.Message}");
}
}
private string GetLastConnectedDeviceAddress()
{
try
{
// 使用MAUI的Preferences API获取设备地址
return Microsoft.Maui.Storage.Preferences.Get("LastConnectedDevice", null);
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"GetLastConnectedDeviceAddress Error: {ex.Message}");
return null;
}
}
private void SaveLastConnectedDeviceAddress(string deviceAddress)
{
try
{
var sharedPreferences = GetSharedPreferences("HeartRateMonitor", FileCreationMode.Private);
var editor = sharedPreferences.Edit();
editor.PutString("LastConnectedDevice", deviceAddress);
editor.Apply();
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 保存设备地址: {deviceAddress}");
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"SaveLastConnectedDeviceAddress Error: {ex.Message}");
}
}
private string GetServerUrl()
{
try
{
// 从Resources/Raw/token.txt读取服务器URL
using var stream = FileSystem.OpenAppPackageFileAsync("server.txt").Result;
using var reader = new StreamReader(stream);
var contents = reader.ReadToEnd();
return contents;
}
catch
{
return "wss:///ws.nuanr-mxi.com/ws"; // 默认URL
}
}
private void StartHeartRateMonitoring()
{
_heartRateTimer = new System.Timers.Timer(1000); // 30秒间隔
_heartRateTimer.Elapsed += async (sender, e) =>
{
try
{
await MonitorHeartRate();
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Heart rate monitoring error: {ex.Message}");
}
};
_heartRateTimer.Start();
}
private async Task MonitorHeartRate()
{
try
{
// 确保WebSocket连接
if (_webSocketClient != null)
{
await _webSocketClient.ConnectAsync();
// 从蓝牙设备获取心率数据
var heartRate = await GetCurrentHeartRate();
if (heartRate > 0)
{
await _webSocketClient.SendHeartRateAsync(heartRate);
UpdateNotification($"最新心率: {heartRate} BPM");
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"MonitorHeartRate Error: {ex.Message}");
}
}
private async Task<int> GetCurrentHeartRate()
{
try
{
// 只从蓝牙设备获取真实心率数据
if (_bluetoothService?.ConnectedDevice != null)
{
// 从BluetoothService获取最新的心率值
return _bluetoothService.LastHeartRate;
}
// 如果没有连接设备返回0表示无数据
return 0;
}
catch
{
return 0;
}
}
private void UpdateNotification(string message)
{
try
{
var intent = new Intent(this, typeof(MainActivity));
var pendingIntent = PendingIntent.GetActivity(this, 0, intent,
PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.SetContentTitle("心率监测服务")
.SetContentText(message)
.SetSmallIcon(Resource.Drawable.abc_dialog_material_background) // 使用系统图标
.SetContentIntent(pendingIntent)
.SetOngoing(true)
.Build();
var notificationManager = GetSystemService(NotificationService) as NotificationManager;
notificationManager?.Notify(NOTIFICATION_ID, notification);
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"UpdateNotification Error: {ex.Message}");
}
}
private void ScheduleJobService()
{
try
{
var jobScheduler = GetSystemService(JobSchedulerService) as JobScheduler;
var jobInfo = new JobInfo.Builder(1002, new ComponentName(this, Java.Lang.Class.FromType(typeof(HeartRateJobService))))
.SetRequiredNetworkType(NetworkType.Any)
.SetPersisted(true)
.SetPeriodic(15 * 60 * 1000) // 15分钟
.SetRequiresCharging(false)
.SetRequiresDeviceIdle(false)
.Build();
jobScheduler?.Schedule(jobInfo);
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"ScheduleJobService Error: {ex.Message}");
}
}
public override void OnDestroy()
{
_isServiceRunning = false;
_heartRateTimer?.Stop();
_heartRateTimer?.Dispose();
_webSocketClient?.Dispose();
_wakeLock?.Release();
// 服务被销毁时,立即重启
RestartService();
base.OnDestroy();
}
private void RestartService()
{
try
{
var intent = new Intent(this, typeof(HeartRateKeepAliveService));
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
StartForegroundService(intent);
}
else
{
StartService(intent);
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"RestartService Error: {ex.Message}");
}
}
public override void OnTaskRemoved(Intent rootIntent)
{
// 当任务被移除时重启服务
RestartService();
base.OnTaskRemoved(rootIntent);
}
}
}

View File

@ -0,0 +1,155 @@
using Android.App;
using Android.App.Job;
using Android.Content;
using Android.OS;
namespace HeartRateMonitorAndroid.Platforms.Android
{
[BroadcastReceiver(Name = "com.nuanrmxi.heartratemonitor.KeepAliveBroadcastReceiver",
Enabled = true,
Exported = true)]
[IntentFilter(new[] {
Intent.ActionBootCompleted,
Intent.ActionUserPresent,
Intent.ActionMyPackageReplaced,
Intent.ActionPackageReplaced,
"android.net.conn.CONNECTIVITY_CHANGE",
Intent.ActionScreenOn,
Intent.ActionScreenOff
}, Priority = 1000)]
public class KeepAliveBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
var action = intent?.Action;
System.Diagnostics.Debug.WriteLine($"KeepAliveBroadcastReceiver: 收到广播 {action}");
try
{
switch (action)
{
case Intent.ActionBootCompleted:
System.Diagnostics.Debug.WriteLine("设备启动完成,启动心率监测服务");
StartHeartRateService(context);
break;
case Intent.ActionUserPresent:
System.Diagnostics.Debug.WriteLine("用户解锁设备,检查服务状态");
CheckAndStartService(context);
break;
case Intent.ActionMyPackageReplaced:
case Intent.ActionPackageReplaced:
System.Diagnostics.Debug.WriteLine("应用包更新,重启服务");
StartHeartRateService(context);
break;
case "android.net.conn.CONNECTIVITY_CHANGE":
System.Diagnostics.Debug.WriteLine("网络连接状态改变,检查服务");
CheckAndStartService(context);
break;
case Intent.ActionScreenOn:
System.Diagnostics.Debug.WriteLine("屏幕亮起,检查服务状态");
CheckAndStartService(context);
break;
case Intent.ActionScreenOff:
System.Diagnostics.Debug.WriteLine("屏幕关闭,确保服务运行");
CheckAndStartService(context);
break;
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"KeepAliveBroadcastReceiver Error: {ex.Message}");
}
}
private void StartHeartRateService(Context context)
{
try
{
var intent = new Intent(context, typeof(HeartRateKeepAliveService));
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
context.StartForegroundService(intent);
}
else
{
context.StartService(intent);
}
// 同时启动JobScheduler
ScheduleJobService(context);
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"StartHeartRateService Error: {ex.Message}");
}
}
private void CheckAndStartService(Context context)
{
try
{
if (!IsServiceRunning(context, typeof(HeartRateKeepAliveService)))
{
System.Diagnostics.Debug.WriteLine("服务未运行,正在启动...");
StartHeartRateService(context);
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"CheckAndStartService Error: {ex.Message}");
}
}
private bool IsServiceRunning(Context context, System.Type serviceType)
{
try
{
var activityManager = context.GetSystemService(Context.ActivityService) as ActivityManager;
var services = activityManager?.GetRunningServices(int.MaxValue);
if (services != null)
{
foreach (var service in services)
{
if (service.Service.ClassName.Contains(serviceType.Name))
{
return true;
}
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"IsServiceRunning Error: {ex.Message}");
}
return false;
}
private void ScheduleJobService(Context context)
{
try
{
var jobScheduler = context.GetSystemService(Context.JobSchedulerService) as JobScheduler;
var jobInfo = new JobInfo.Builder(1002, new ComponentName(context, Java.Lang.Class.FromType(typeof(HeartRateJobService))))
.SetRequiredNetworkType(NetworkType.Any)
.SetPersisted(true)
.SetPeriodic(15 * 60 * 1000) // 15分钟
.SetRequiresCharging(false)
.SetRequiresDeviceIdle(false)
.Build();
jobScheduler?.Schedule(jobInfo);
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"ScheduleJobService Error: {ex.Message}");
}
}
}
}

View File

@ -0,0 +1,325 @@
using Android.App;
using Android.App.Job;
using Android.Content;
using Android.OS;
using Android.Provider;
using AndroidX.Core.App;
using Microsoft.Maui.Controls.PlatformConfiguration;
using Uri = Android.Net.Uri;
namespace HeartRateMonitorAndroid.Platforms.Android
{
/// <summary>
/// 保活管理器,负责统一管理所有保活功能
/// </summary>
public static class KeepAliveManager
{
private static bool _isInitialized = false;
/// <summary>
/// 初始化保活功能
/// </summary>
public static void Initialize(Context context)
{
if (_isInitialized) return;
try
{
System.Diagnostics.Debug.WriteLine("KeepAliveManager: 开始初始化保活功能");
// 1. 启动前台服务
StartForegroundService(context);
// 2. 调度JobScheduler任务
ScheduleJobService(context);
// 3. 注册广播接收器(通过代码方式补充)
RegisterBroadcastReceiver(context);
// 4. 请求系统权限
RequestSystemPermissions(context);
_isInitialized = true;
System.Diagnostics.Debug.WriteLine("KeepAliveManager: 保活功能初始化完成");
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"KeepAliveManager Initialize Error: {ex.Message}");
}
}
/// <summary>
/// 启动前台服务
/// </summary>
public static void StartForegroundService(Context context)
{
try
{
var intent = new Intent(context, typeof(HeartRateKeepAliveService));
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
context.StartForegroundService(intent);
}
else
{
context.StartService(intent);
}
System.Diagnostics.Debug.WriteLine("KeepAliveManager: 前台服务已启动");
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"StartForegroundService Error: {ex.Message}");
}
}
/// <summary>
/// 调度JobScheduler任务
/// </summary>
public static void ScheduleJobService(Context context)
{
try
{
var jobScheduler = context.GetSystemService(Context.JobSchedulerService) as JobScheduler;
var jobInfo = new JobInfo.Builder(1002, new ComponentName(context, Java.Lang.Class.FromType(typeof(HeartRateJobService))))
.SetRequiredNetworkType(NetworkType.Any)
.SetPersisted(true)
.SetPeriodic(15 * 60 * 1000) // 15分钟
.SetRequiresCharging(false)
.SetRequiresDeviceIdle(false)
.Build();
var result = jobScheduler?.Schedule(jobInfo);
System.Diagnostics.Debug.WriteLine($"KeepAliveManager: JobScheduler调度结果: {result}");
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"ScheduleJobService Error: {ex.Message}");
}
}
/// <summary>
/// 动态注册广播接收器
/// </summary>
public static void RegisterBroadcastReceiver(Context context)
{
try
{
var receiver = new KeepAliveBroadcastReceiver();
var filter = new IntentFilter();
// 添加需要监听的动作
filter.AddAction(Intent.ActionUserPresent);
filter.AddAction("android.net.conn.CONNECTIVITY_CHANGE");
filter.AddAction(Intent.ActionScreenOn);
filter.AddAction(Intent.ActionScreenOff);
filter.Priority = 1000;
if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu)
{
context.RegisterReceiver(receiver, filter, ReceiverFlags.Exported);
}
else
{
context.RegisterReceiver(receiver, filter);
}
System.Diagnostics.Debug.WriteLine("KeepAliveManager: 动态广播接收器已注册");
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"RegisterBroadcastReceiver Error: {ex.Message}");
}
}
/// <summary>
/// 请求系统权限
/// </summary>
public static void RequestSystemPermissions(Context context)
{
try
{
// 请求忽略电池优化
RequestIgnoreBatteryOptimization(context);
// 请求自启动权限(针对不同厂商)
RequestAutoStartPermission(context);
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"RequestSystemPermissions Error: {ex.Message}");
}
}
/// <summary>
/// 请求忽略电池优化
/// </summary>
public static void RequestIgnoreBatteryOptimization(Context context)
{
try
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
var powerManager = context.GetSystemService(Context.PowerService) as PowerManager;
if (powerManager != null && !powerManager.IsIgnoringBatteryOptimizations(context.PackageName))
{
var intent = new Intent(Settings.ActionRequestIgnoreBatteryOptimizations);
intent.SetData(Uri.Parse($"package:{context.PackageName}"));
intent.SetFlags(ActivityFlags.NewTask);
if (intent.ResolveActivity(context.PackageManager) != null)
{
context.StartActivity(intent);
System.Diagnostics.Debug.WriteLine("KeepAliveManager: 请求电池优化豁免");
}
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"RequestIgnoreBatteryOptimization Error: {ex.Message}");
}
}
/// <summary>
/// 请求自启动权限(针对不同手机厂商)
/// </summary>
public static void RequestAutoStartPermission(Context context)
{
try
{
var manufacturer = Build.Manufacturer?.ToLower();
Intent intent = null;
switch (manufacturer)
{
case "xiaomi":
intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
intent.SetClassName("com.miui.securitycenter",
"com.miui.permcenter.autostart.AutoStartManagementActivity");
intent.PutExtra("extra_pkgname", context.PackageName);
break;
case "oppo":
intent = new Intent();
intent.SetClassName("com.coloros.safecenter",
"com.coloros.safecenter.permission.startup.StartupAppListActivity");
break;
case "vivo":
intent = new Intent();
intent.SetClassName("com.vivo.permissionmanager",
"com.vivo.permissionmanager.activity.BgStartUpManagerActivity");
break;
case "honor":
case "huawei":
intent = new Intent();
intent.SetClassName("com.huawei.systemmanager",
"com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
break;
case "oneplus":
intent = new Intent();
intent.SetClassName("com.oneplus.security",
"com.oneplus.security.chainlaunch.view.ChainLaunchAppListActivity");
break;
case "letv":
intent = new Intent();
intent.SetClassName("com.letv.android.letvsafe",
"com.letv.android.letvsafe.AutobootManageActivity");
break;
}
if (intent != null)
{
intent.SetFlags(ActivityFlags.NewTask);
if (intent.ResolveActivity(context.PackageManager) != null)
{
context.StartActivity(intent);
System.Diagnostics.Debug.WriteLine($"KeepAliveManager: 请求{manufacturer}自启动权限");
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"RequestAutoStartPermission Error: {ex.Message}");
}
}
/// <summary>
/// 检查服务是否运行
/// </summary>
public static bool IsServiceRunning(Context context, System.Type serviceType)
{
try
{
var activityManager = context.GetSystemService(Context.ActivityService) as ActivityManager;
var services = activityManager?.GetRunningServices(int.MaxValue);
if (services != null)
{
foreach (var service in services)
{
if (service.Service.ClassName.Contains(serviceType.Name))
{
return true;
}
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"IsServiceRunning Error: {ex.Message}");
}
return false;
}
/// <summary>
/// 重启所有保活服务
/// </summary>
public static void RestartKeepAliveServices(Context context)
{
try
{
System.Diagnostics.Debug.WriteLine("KeepAliveManager: 重启保活服务");
// 重启前台服务
StartForegroundService(context);
// 重新调度Job
ScheduleJobService(context);
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"RestartKeepAliveServices Error: {ex.Message}");
}
}
/// <summary>
/// 停止所有保活服务
/// </summary>
public static void StopKeepAliveServices(Context context)
{
try
{
System.Diagnostics.Debug.WriteLine("KeepAliveManager: 停止保活服务");
// 停止前台服务
var intent = new Intent(context, typeof(HeartRateKeepAliveService));
context.StopService(intent);
// 取消Job调度
var jobScheduler = context.GetSystemService(Context.JobSchedulerService) as JobScheduler;
jobScheduler?.Cancel(1002);
_isInitialized = false;
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"StopKeepAliveServices Error: {ex.Message}");
}
}
}
}

View File

@ -1,6 +1,11 @@
using Android.App;
using Android.Content.PM;
using Android.OS;
using Android.Content;
using Android.Provider;
using AndroidX.Core.App;
using AndroidX.Core.Content;
using HeartRateMonitorAndroid.Platforms.Android;
namespace HeartRateMonitorAndroid;
@ -9,4 +14,178 @@ namespace HeartRateMonitorAndroid;
ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
private const int BATTERY_OPTIMIZATION_REQUEST = 1001;
private const int NOTIFICATION_PERMISSION_REQUEST = 1002;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// 初始化保活功能
InitializeKeepAlive();
}
private void InitializeKeepAlive()
{
try
{
// 请求忽略电池优化
RequestIgnoreBatteryOptimization();
// 请求通知权限Android 13+
RequestNotificationPermission();
// 启动前台服务
StartHeartRateKeepAliveService();
System.Diagnostics.Debug.WriteLine("保活功能初始化完成");
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"InitializeKeepAlive Error: {ex.Message}");
}
}
private void RequestIgnoreBatteryOptimization()
{
try
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
var powerManager = GetSystemService(PowerService) as PowerManager;
if (powerManager != null && !powerManager.IsIgnoringBatteryOptimizations(PackageName))
{
var intent = new Intent(Settings.ActionRequestIgnoreBatteryOptimizations);
intent.SetData(Android.Net.Uri.Parse($"package:{PackageName}"));
StartActivityForResult(intent, BATTERY_OPTIMIZATION_REQUEST);
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"RequestIgnoreBatteryOptimization Error: {ex.Message}");
}
}
private void RequestNotificationPermission()
{
try
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu)
{
if (ContextCompat.CheckSelfPermission(this, Android.Manifest.Permission.PostNotifications)
!= Permission.Granted)
{
ActivityCompat.RequestPermissions(this,
new[] { Android.Manifest.Permission.PostNotifications },
NOTIFICATION_PERMISSION_REQUEST);
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"RequestNotificationPermission Error: {ex.Message}");
}
}
private void StartHeartRateKeepAliveService()
{
try
{
var intent = new Intent(this, typeof(HeartRateKeepAliveService));
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
StartForegroundService(intent);
}
else
{
StartService(intent);
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"StartHeartRateKeepAliveService Error: {ex.Message}");
}
}
protected override void OnResume()
{
base.OnResume();
// 每次应用恢复时检查服务状态
CheckServiceStatus();
}
private void CheckServiceStatus()
{
try
{
if (!IsServiceRunning(typeof(HeartRateKeepAliveService)))
{
System.Diagnostics.Debug.WriteLine("检测到服务未运行,正在重启...");
StartHeartRateKeepAliveService();
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"CheckServiceStatus Error: {ex.Message}");
}
}
private bool IsServiceRunning(System.Type serviceType)
{
try
{
var activityManager = GetSystemService(ActivityService) as ActivityManager;
var services = activityManager?.GetRunningServices(int.MaxValue);
if (services != null)
{
foreach (var service in services)
{
if (service.Service.ClassName.Contains(serviceType.Name))
{
return true;
}
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"IsServiceRunning Error: {ex.Message}");
}
return false;
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode)
{
case NOTIFICATION_PERMISSION_REQUEST:
if (grantResults.Length > 0 && grantResults[0] == Permission.Granted)
{
System.Diagnostics.Debug.WriteLine("通知权限已授予");
}
else
{
System.Diagnostics.Debug.WriteLine("通知权限被拒绝");
}
break;
}
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
switch (requestCode)
{
case BATTERY_OPTIMIZATION_REQUEST:
System.Diagnostics.Debug.WriteLine($"电池优化请求结果: {resultCode}");
break;
}
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorOnPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C13.1,2 14,2.9 14,4C14,5.1 13.1,6 12,6C10.9,6 10,5.1 10,4C10,2.9 10.9,2 12,2ZM21,9V7L15,13.5C14.8,13.8 14.5,14 14.1,14H9.9C9.5,14 9.2,13.8 9,13.5L3,7V9C3,9.6 3.4,10 4,10H20C20.6,10 21,9.6 21,9ZM12,15C13.1,15 14,15.9 14,17C14,18.1 13.1,19 12,19C10.9,19 10,18.1 10,17C10,15.9 10.9,15 12,15ZM12,20C13.1,20 14,20.9 14,22C14,23.1 13.1,24 12,24C10.9,24 10,23.1 10,22C10,20.9 10.9,20 12,20Z"/>
</vector>