| | |
| | | import Settings from 'android.provider.Settings' |
| | | import { UTSAndroid } from 'io.dcloud.uts' |
| | | import BatteryManager from 'android.os.BatteryManager' |
| | | 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 KeyguardManager from 'android.app.KeyguardManager' |
| | | |
| | | // 来电通知相关常量 |
| | | const CALL_CHANNEL_ID = 'incoming_call_channel' |
| | | const CALL_NOTIFICATION_ID: Int = 2001 |
| | | |
| | | // 拉起应用 |
| | | export function openDialog(): void { |
| | |
| | | // 1. 检查悬浮窗权限 (Android 6.0+) |
| | | const noneFloat = Build.VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(context) |
| | | if (noneFloat) { |
| | | uni.showToast({ title: '请允许应用悬浮在屏幕上', icon: 'none' }) |
| | | } else { |
| | | setTimeout(() => { |
| | | try { |
| | | // 无权限,跳转到权限设置页面 |
| | | const intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) |
| | | intent.setData(android.net.Uri.parse('package:' + context.getPackageName())) |
| | | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) |
| | | context.startActivity(intent) |
| | | } catch (e) { |
| | | console.error(e) |
| | | } |
| | | }, 2000) |
| | | uni.showModal({ |
| | | title: '权限提示', |
| | | content: '为了在后台收到来电时能弹出通话界面,请开启“悬浮”权限', |
| | | confirmText: '去设置', |
| | | cancelText: '暂不', |
| | | success(res) { |
| | | if (res.confirm) { |
| | | try { |
| | | // 无权限,跳转到权限设置页面 |
| | | const intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) |
| | | intent.setData(android.net.Uri.parse('package:' + context.getPackageName())) |
| | | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) |
| | | context.startActivity(intent) |
| | | } catch (e) { |
| | | console.error(e) |
| | | } |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | |
| | |
| | | return '0%' |
| | | } |
| | | |
| | | /** |
| | | * 显示来电通知(锁屏/后台可弹出) |
| | | * 利用高优先级通知 + fullScreenIntent 实现锁屏/后台来电提醒 |
| | | * @param callerName 来电者名称 |
| | | */ |
| | | export function showIncomingCallNotification(callerName: string): void { |
| | | const context = UTSAndroid.getAppContext() |
| | | if (context == null) return |
| | | |
| | | try { |
| | | // 1. 创建来电通知渠道(IMPORTANCE_HIGH → Heads-up 弹出) |
| | | if (Build.VERSION.SDK_INT >= 26) { |
| | | const channel = new NotificationChannel( |
| | | CALL_CHANNEL_ID, |
| | | '来电通知', |
| | | NotificationManager.IMPORTANCE_HIGH |
| | | ) |
| | | channel.setDescription('语音来电通知') |
| | | channel.enableVibration(true) |
| | | channel.enableLights(true) |
| | | channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC) |
| | | channel.setBypassDnd(true) |
| | | |
| | | const nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager |
| | | nm.createNotificationChannel(channel) |
| | | } |
| | | |
| | | // 2. 构建点击通知后打开应用的 Intent |
| | | const pm = context.getPackageManager() |
| | | const launchIntent = pm.getLaunchIntentForPackage(context.getPackageName()) |
| | | if (launchIntent != null) { |
| | | launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) |
| | | launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) |
| | | } |
| | | |
| | | let pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT |
| | | if (Build.VERSION.SDK_INT >= 31) { |
| | | pendingIntentFlags = pendingIntentFlags | PendingIntent.FLAG_IMMUTABLE |
| | | } |
| | | |
| | | const contentIntent = PendingIntent.getActivity(context, 0, launchIntent, pendingIntentFlags) |
| | | // fullScreenIntent 用于锁屏时全屏展示 |
| | | const fullScreenIntent = PendingIntent.getActivity(context, 1, launchIntent, pendingIntentFlags) |
| | | |
| | | // 3. 构建通知 |
| | | const appInfo = context.getApplicationInfo() |
| | | const appIcon = appInfo.icon |
| | | |
| | | let builder: Notification.Builder |
| | | if (Build.VERSION.SDK_INT >= 26) { |
| | | builder = new Notification.Builder(context, CALL_CHANNEL_ID) |
| | | } else { |
| | | builder = new Notification.Builder(context) |
| | | } |
| | | |
| | | builder |
| | | .setSmallIcon(appIcon) |
| | | .setContentTitle('语音来电') |
| | | .setContentText(callerName + ' 正在呼叫您') |
| | | .setContentIntent(contentIntent) |
| | | .setFullScreenIntent(fullScreenIntent, true) // 🔑 锁屏全屏弹出 |
| | | .setOngoing(true) |
| | | .setAutoCancel(false) |
| | | .setPriority(Notification.PRIORITY_MAX) |
| | | .setVisibility(Notification.VISIBILITY_PUBLIC) // 锁屏显示完整内容 |
| | | |
| | | if (Build.VERSION.SDK_INT >= 21) { |
| | | builder.setCategory(Notification.CATEGORY_CALL) // 标记为来电类型 |
| | | } |
| | | |
| | | |
| | | |
| | | const notification = builder.build() |
| | | // 设置通知标志:保持常亮、不可清除 |
| | | notification.flags = notification.flags | Notification.FLAG_INSISTENT | Notification.FLAG_NO_CLEAR |
| | | |
| | | // 4. 发送通知 |
| | | const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager |
| | | notificationManager.notify(CALL_NOTIFICATION_ID, notification) |
| | | |
| | | // 5. 如果在锁屏状态,尝试亮屏 |
| | | wakeUpScreen(context) |
| | | |
| | | console.log('showIncomingCallNotification: 来电通知已发送,来电者:', callerName) |
| | | } catch (e) { |
| | | console.error('showIncomingCallNotification: 发送来电通知失败', e) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 取消来电通知 |
| | | * 在接听或拒接后调用 |
| | | */ |
| | | export function cancelIncomingCallNotification(): void { |
| | | const context = UTSAndroid.getAppContext() |
| | | if (context == null) return |
| | | |
| | | try { |
| | | const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager |
| | | notificationManager.cancel(CALL_NOTIFICATION_ID) |
| | | console.log('cancelIncomingCallNotification: 来电通知已取消') |
| | | } catch (e) { |
| | | console.error('cancelIncomingCallNotification: 取消来电通知失败', e) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 唤醒屏幕(锁屏时亮屏) |
| | | */ |
| | | function wakeUpScreen(context: Context): void { |
| | | try { |
| | | const powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager |
| | | if (!powerManager.isInteractive()) { |
| | | // 屏幕关闭,唤醒屏幕 |
| | | const screenLock = powerManager.newWakeLock( |
| | | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, |
| | | 'IncomingCall::WakeUp' |
| | | ) |
| | | screenLock.acquire(10000) // 保持亮屏 10 秒 |
| | | } |
| | | } catch (e) { |
| | | console.error('wakeUpScreen: 唤醒屏幕失败', e) |
| | | } |
| | | } |