Android 12/13实现类WhatsApp/TrueCaller来电弹窗与全屏意图求助
Android 12/13 实现来电通知弹窗及全屏意图方案
1. 权限配置
- 必须申请
POST_NOTIFICATIONS权限(Android 13+),在Manifest中声明:
同时保留<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />READ_PHONE_STATE、FOREGROUND_SERVICE权限,Android 12+需为前台服务指定类型:<service android:name=".CallForegroundService" android:foregroundServiceType="phoneCall" /> - 运行时动态请求
POST_NOTIFICATIONS和READ_PHONE_STATE权限,无权限无法触发通知或监听来电。
2. 替换广播接收器的来电监听方案
Android 12+对隐式广播接收器限制严格,需改为动态监听:
- 使用
PhoneStateListener在前台服务中监听来电状态,避免后台监听被系统回收:val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager val phoneStateListener = object : PhoneStateListener() { override fun onCallStateChanged(state: Int, phoneNumber: String?) { super.onCallStateChanged(state, phoneNumber) if (state == TelephonyManager.CALL_STATE_RINGING && phoneNumber != null) { // 触发来电通知弹窗逻辑 showCallNotification(phoneNumber) } } } // 在前台服务的onStartCommand中注册监听 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE)
3. 前台服务的合规启动
Android 12+启动前台服务需满足触发场景(如来电事件),启动后必须立即调用startForeground:
// 启动前台服务 val serviceIntent = Intent(context, CallForegroundService::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent) } else { context.startService(serviceIntent) } // 服务内部启动前台 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val foregroundNotification = NotificationCompat.Builder(this, "FOREGROUND_CHANNEL") .setContentTitle("来电识别中") .setSmallIcon(R.drawable.ic_notification) .setPriority(NotificationCompat.PRIORITY_LOW) .build() startForeground(1, foregroundNotification, ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL) return START_STICKY }
4. 高优先级通知+全屏意图实现弹窗
这是实现类似WhatsApp/TrueCaller来电弹窗的核心:
- 先创建高优先级通知渠道(Android 8.0+):
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( "CALL_CHANNEL", "来电通知", NotificationManager.IMPORTANCE_HIGH ).apply { setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE), null) enableVibration(true) } val notificationManager = getSystemService(NotificationManager::class.java) notificationManager.createNotificationChannel(channel) } - 构建带全屏意图的来电通知:
// 全屏弹窗Activity的PendingIntent val fullScreenIntent = Intent(this, CallPopupActivity::class.java).apply { putExtra("PHONE_NUMBER", phoneNumber) } val fullScreenPendingIntent = PendingIntent.getActivity( this, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // 构建通知 val notification = NotificationCompat.Builder(this, "CALL_CHANNEL") .setContentTitle("陌生来电") .setContentText(phoneNumber) .setSmallIcon(R.drawable.ic_call) .setPriority(NotificationCompat.PRIORITY_HIGH) .setCategory(NotificationCompat.CATEGORY_CALL) .setFullScreenIntent(fullScreenPendingIntent, true) // 添加接听/挂断操作按钮 .addAction( R.drawable.ic_accept, "接听", PendingIntent.getService(this, 1, acceptIntent, PendingIntent.FLAG_IMMUTABLE) ) .addAction( R.drawable.ic_reject, "挂断", PendingIntent.getService(this, 2, rejectIntent, PendingIntent.FLAG_IMMUTABLE) ) .build() // 显示通知 val notificationManager = getSystemService(NotificationManager::class.java) notificationManager.notify(2, notification)
5. 来电弹窗Activity配置
在Manifest中配置Activity属性,确保锁屏可见、全屏显示:
<activity android:name=".CallPopupActivity" android:showOnLockScreen="true" android:showWhenLocked="true" android:turnScreenOn="true" android:excludeFromRecents="true" android:launchMode="singleInstance" android:theme="@style/CallPopupTheme"> </activity>
对应的主题设置:
<style name="CallPopupTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowFullscreen">true</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowBackground">@android:color/transparent</item> </style>
关键注意事项
- Android 12+中,全屏意图对应的Activity必须有可见内容,不能完全透明,否则系统会拦截。
- 通知渠道必须设置为
IMPORTANCE_HIGH,否则无法触发全屏弹窗。 - 前台服务类型必须指定为
phoneCall,避免被系统限制启动。
内容的提问来源于stack exchange,提问作者user1847921
相关产品推荐
相关产品推荐

