// index.uts - Android 应用保活插件
|
|
// 引用 Android API
|
import Context from 'android.content.Context'
|
import Intent from 'android.content.Intent'
|
import Build from 'android.os.Build'
|
import NotificationChannel from 'android.app.NotificationChannel'
|
import NotificationManager from 'android.app.NotificationManager'
|
import Notification from 'android.app.Notification'
|
import PendingIntent from 'android.app.PendingIntent'
|
import PowerManager from 'android.os.PowerManager'
|
import BatteryManager from 'android.os.BatteryManager'
|
import { UTSAndroid } from 'io.dcloud.uts'
|
import AlertDialog from 'android.app.AlertDialog'
|
import DialogInterface from 'android.content.DialogInterface'
|
|
// 保活服务的通知渠道ID
|
const CHANNEL_ID = 'keep_alive_channel'
|
const NOTIFICATION_ID: Int = 1001
|
|
// 保活服务是否已启动的标志
|
let isKeepAliveRunning = false
|
|
// WakeLock 引用,用于保持 CPU 运行
|
let wakeLock: PowerManager.WakeLock | null = null
|
|
/**
|
* 启动应用保活
|
* 通过 WakeLock + 通知来保持应用在后台运行
|
*/
|
export function keepAliveStart(): void {
|
const context = UTSAndroid.getAppContext()
|
if (context == null) {
|
console.error('keepAliveStart: 获取应用上下文失败')
|
return
|
}
|
|
if (isKeepAliveRunning) {
|
console.log('keepAliveStart: 保活服务已在运行中')
|
return
|
}
|
|
try {
|
// 1. 获取 WakeLock 唤醒锁,保持 CPU 运行
|
acquireWakeLock(context)
|
|
// 2. 创建通知渠道 (Android 8.0+)
|
createNotificationChannel(context)
|
|
// 3. 创建并显示前台通知
|
showForegroundNotification(context)
|
|
// 4. 注册应用生命周期监听
|
registerLifecycleCallbacks()
|
|
isKeepAliveRunning = true
|
console.log('keepAliveStart: 保活服务启动成功(WakeLock已获取)')
|
} catch (e) {
|
console.error('keepAliveStart: 启动失败', e)
|
}
|
}
|
|
/**
|
* 停止应用保活
|
*/
|
export function keepAliveStop(): void {
|
const context = UTSAndroid.getAppContext()
|
if (context == null) {
|
return
|
}
|
|
try {
|
// 释放 WakeLock
|
releaseWakeLock()
|
|
// 取消通知
|
const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
notificationManager.cancel(NOTIFICATION_ID)
|
|
isKeepAliveRunning = false
|
console.log('keepAliveStop: 保活服务已停止(WakeLock已释放)')
|
} catch (e) {
|
console.error('keepAliveStop: 停止失败', e)
|
}
|
}
|
|
/**
|
* 检查保活服务是否正在运行
|
*/
|
export function isKeepAliveActive(): boolean {
|
return isKeepAliveRunning
|
}
|
|
/**
|
* 获取电池电量百分比
|
*/
|
export function getBatteryCapacity(): string {
|
const context = UTSAndroid.getAppContext()
|
if (context != null) {
|
const manager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
|
const currentLevel: number = manager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
|
return '' + currentLevel + '%'
|
}
|
return '0%'
|
}
|
|
|
|
/**
|
* 检查是否已忽略电池优化
|
*/
|
export function isIgnoringBatteryOptimizations(): boolean {
|
const context = UTSAndroid.getAppContext()
|
if (context == null) {
|
return false
|
}
|
|
if (Build.VERSION.SDK_INT >= 23) {
|
const powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
const packageName = context.getPackageName()
|
return powerManager.isIgnoringBatteryOptimizations(packageName)
|
}
|
|
return true // Android 6.0 以下默认返回 true
|
}
|
|
/**
|
* 请求忽略电池优化(需要用户手动确认)
|
* 调用此方法会弹出系统设置页面
|
*/
|
export function requestIgnoreBatteryOptimization(): void {
|
if (isIgnoringBatteryOptimizations()) return
|
const activity = UTSAndroid.getUniActivity()
|
if (activity == null) return
|
AlertDialog.Builder(activity)
|
.setTitle('权限提示')
|
.setMessage('为了在后台收到来电时能弹出通话界面,请将应用添加到电池优化白名单')
|
.setPositiveButton('去设置', (_dialog : DialogInterface, _which : Int) => {
|
const context = UTSAndroid.getAppContext()
|
if (context == null) return
|
if (Build.VERSION.SDK_INT >= 23) {
|
const powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
const packageName = context.getPackageName()
|
if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
|
try {
|
const intent = new Intent()
|
intent.setAction('android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS')
|
intent.setData(android.net.Uri.parse('package:' + packageName))
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
context.startActivity(intent)
|
} catch (e) {
|
console.error('requestIgnoreBatteryOptimization: 打开设置失败', e)
|
}
|
}
|
}
|
})
|
.setNegativeButton('暂不', null)
|
.show()
|
}
|
|
/**
|
* 创建通知渠道 (Android 8.0+ 必需)
|
*/
|
function createNotificationChannel(context: Context): void {
|
if (Build.VERSION.SDK_INT >= 26) {
|
// Android 8.0+
|
const channel = new NotificationChannel(CHANNEL_ID, '应用保活服务', NotificationManager.IMPORTANCE_LOW)
|
channel.setDescription('保持应用在后台运行')
|
channel.setShowBadge(false)
|
channel.enableLights(false)
|
channel.enableVibration(false)
|
|
const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
notificationManager.createNotificationChannel(channel)
|
}
|
}
|
|
/**
|
* 显示前台通知
|
*/
|
function showForegroundNotification(context: Context): void {
|
// 获取应用的启动 Intent
|
const packageManager = context.getPackageManager()
|
const launchIntent = packageManager.getLaunchIntentForPackage(context.getPackageName())
|
|
let pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT
|
if (Build.VERSION.SDK_INT >= 31) {
|
// Android 12+
|
pendingIntentFlags = pendingIntentFlags | PendingIntent.FLAG_IMMUTABLE
|
}
|
|
const pendingIntent = PendingIntent.getActivity(context, 0, launchIntent, pendingIntentFlags)
|
|
// 构建通知
|
let builder: Notification.Builder
|
if (Build.VERSION.SDK_INT >= 26) {
|
builder = new Notification.Builder(context, CHANNEL_ID)
|
} else {
|
builder = new Notification.Builder(context)
|
}
|
|
// 获取应用图标
|
const appInfo = context.getApplicationInfo()
|
const appIcon = appInfo.icon
|
const appName = packageManager.getApplicationLabel(appInfo).toString()
|
|
builder
|
.setSmallIcon(appIcon)
|
.setContentTitle(appName)
|
.setContentText('服务运行中')
|
.setContentIntent(pendingIntent)
|
.setOngoing(true) // 常驻通知,不可被滑动删除
|
.setPriority(Notification.PRIORITY_LOW)
|
|
if (Build.VERSION.SDK_INT >= 21) {
|
builder.setCategory(Notification.CATEGORY_SERVICE)
|
}
|
|
const notification = builder.build()
|
|
// 显示通知
|
const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
notificationManager.notify(NOTIFICATION_ID, notification)
|
}
|
|
/**
|
* 获取 WakeLock 唤醒锁
|
* PARTIAL_WAKE_LOCK: 保持 CPU 运行,屏幕和键盘背光可以关闭
|
*/
|
function acquireWakeLock(context: Context): void {
|
if (wakeLock != null && wakeLock!.isHeld()) {
|
console.log('acquireWakeLock: WakeLock 已持有')
|
return
|
}
|
|
try {
|
const powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
// PARTIAL_WAKE_LOCK 保持 CPU 运行
|
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 'KeepAlive::WakeLock')
|
|
if (wakeLock != null) {
|
// 设置超时时间为无限(传 0 或负数表示无限)
|
// 使用 acquire() 无参版本
|
wakeLock!.setReferenceCounted(false)
|
wakeLock!.acquire()
|
console.log('acquireWakeLock: WakeLock 获取成功')
|
}
|
} catch (e) {
|
console.error('acquireWakeLock: WakeLock 获取失败', e)
|
}
|
}
|
|
/**
|
* 释放 WakeLock 唤醒锁
|
*/
|
function releaseWakeLock(): void {
|
if (wakeLock != null && wakeLock!.isHeld()) {
|
try {
|
wakeLock!.release()
|
wakeLock = null
|
console.log('releaseWakeLock: WakeLock 已释放')
|
} catch (e) {
|
console.error('releaseWakeLock: WakeLock 释放失败', e)
|
}
|
}
|
}
|
|
/**
|
* 注册应用生命周期回调
|
* 当应用进入后台时,确保 WakeLock 保持持有
|
*/
|
function registerLifecycleCallbacks(): void {
|
// 应用进入后台时的回调
|
UTSAndroid.onAppActivityPause(() => {
|
console.log('onAppActivityPause: 应用进入后台')
|
// 确保 WakeLock 仍然持有
|
const context = UTSAndroid.getAppContext()
|
if (context != null && isKeepAliveRunning) {
|
if (wakeLock == null || !wakeLock!.isHeld()) {
|
acquireWakeLock(context)
|
}
|
}
|
})
|
|
// 应用返回前台时的回调
|
UTSAndroid.onAppActivityResume(() => {
|
console.log('onAppActivityResume: 应用返回前台')
|
})
|
|
// 应用被销毁时的回调
|
UTSAndroid.onAppActivityDestroy(() => {
|
console.log('onAppActivityDestroy: 应用被销毁')
|
// 释放资源
|
releaseWakeLock()
|
isKeepAliveRunning = false
|
})
|
|
console.log('registerLifecycleCallbacks: 生命周期回调已注册')
|
}
|