Android非锁屏状态下showFullScreenIntent无法全屏显示来电界面求助
showFullScreenIntent仅显示Heads-up通知的问题 我之前也碰到过一模一样的问题——Android系统默认在设备未锁屏时,setFullScreenIntent只会弹出heads-up通知,不会直接拉起全屏的来电界面。这是系统的行为限制,不过我们可以通过几个关键配置绕过这个限制,让来电界面在任何状态下都能全屏显示。
下面结合你的代码给出具体修改步骤:
1. 申请必要的系统权限
首先,你的App需要获得在其他应用之上显示和唤醒屏幕的权限。在AndroidManifest.xml中添加:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
注意SYSTEM_ALERT_WINDOW是特殊权限,无法在安装时自动授予,你需要在App首次启动时引导用户开启:
private const val OVERLAY_PERMISSION_REQUEST_CODE = 1001 // 在App启动或首次调用来电功能前调用 fun requestOverlayPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) { val intent = Intent( Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName") ) startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE) } }
2. 配置来电Activity的窗口属性
你的CallingScreen需要设置窗口标记,确保它能在锁屏/非锁屏状态下全屏显示,并且覆盖其他应用。在CallingScreen的onCreate方法中添加:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 设置全屏及锁屏相关标记 window.setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON ) // Android 8+ 设置窗口类型为应用覆盖层 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY) } else { // 低版本使用TYPE_PHONE window.setType(WindowManager.LayoutParams.TYPE_PHONE) } setContentView(R.layout.activity_calling_screen) // 你的其他初始化逻辑... }
这些标记的作用:
FLAG_DISMISS_KEYGUARD:自动解锁屏幕FLAG_SHOW_WHEN_LOCKED:允许Activity在锁屏状态下显示FLAG_TURN_SCREEN_ON:点亮熄灭的屏幕FLAG_KEEP_SCREEN_ON:保持屏幕唤醒直到通话结束TYPE_APPLICATION_OVERLAY:确保Activity能在其他应用之上显示(Android 8+)
3. 优化通知渠道和PendingIntent配置
确保通知渠道的高优先级
检查你的NotificationUtils.createChannel方法,必须把渠道重要性设置为IMPORTANCE_HIGH,这样才能触发heads-up通知并配合全屏意图:
fun createChannel(context: Context, notificationManager: NotificationManager) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( CHANNEL_ID, "Incoming Calls", NotificationManager.IMPORTANCE_HIGH ).apply { description = "Handles incoming call notifications" val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/" + R.raw.rongtone_incoming) setSound(soundUri, AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build()) enableVibration(true) } notificationManager.createNotificationChannel(channel) } }
修复PendingIntent的标记和requestCode
Android 12+对PendingIntent的标记有新要求,同时要确保不同操作的PendingIntent使用不同的requestCode,避免互相覆盖:
// 统一处理PendingIntent标记 val pendingFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE } else { PendingIntent.FLAG_UPDATE_CURRENT } // 全屏启动Activity的PendingIntent val showingCallScreenPendingIntent = PendingIntent.getActivity(this, 0, showingCallScreenIntent, pendingFlags) // 接听按钮的PendingIntent(requestCode用1,和上面区分) val acceptCallPendingIntent = PendingIntent.getActivity(this, 1, acceptIntent, pendingFlags)
4. 调整你的sendFullScreenCall方法
最后,优化通知构建代码,移除不需要的setContentIntent(全屏意图已经会拉起Activity),确保setFullScreenIntent的第二个参数为true:
private fun sendFullScreenCall(callInfoModel: CallInfoModel) { val icon = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher) val showingCallScreenIntent = Intent(applicationContext, CallingScreen::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) action = "Accept" putExtra("callObj", callInfoModel) putExtra(STATE, INCOMING) } val acceptIntent = Intent(applicationContext, SingleCallScreen::class.java).apply { putExtra("callObj", callInfoModel) } val pendingFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE } else { PendingIntent.FLAG_UPDATE_CURRENT } val showingCallScreenPendingIntent = PendingIntent.getActivity(this, 0, showingCallScreenIntent, pendingFlags) val acceptCallPendingIntent = PendingIntent.getActivity(this, 1, acceptIntent, pendingFlags) val dimissCallPendingIntent = DismissCallKolinActivity.getDismissIntent(Random().nextInt(), this@FirebaseMessagingService, callInfoModel) val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + applicationContext.packageName + "/" + R.raw.rongtone_incoming) val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("Incoming call") .setContentText(callInfoModel.CallerDisplay) .setAutoCancel(true) .setOnlyAlertOnce(true) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(icon) .setSound(soundUri) .addAction(R.mipmap.ic_video_enabled, getActionText("Answer", R.color.green_color), acceptCallPendingIntent) .addAction(R.mipmap.ic_clear, getActionText("Dismiss", R.color.red), dimissCallPendingIntent) .setFullScreenIntent(showingCallScreenPendingIntent, true) .setPriority(NotificationCompat.PRIORITY_MAX) // 适配Android 7及以下的高优先级 val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notificationManager.getNotificationChannel(CHANNEL_ID)?.let { notificationManager.deleteNotificationChannel(it.id) } } NotificationUtils.createChannel(this@FirebaseMessagingService, notificationManager) notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()) }
完成这些修改后,你的来电界面应该就能在锁屏和非锁屏状态下都全屏显示了。核心是通过权限和窗口属性让Activity能覆盖其他应用,同时确保通知的高优先级触发全屏意图。
内容的提问来源于stack exchange,提问作者famfamfam

