Full-Screen Intent Notifications – Android
What are Full-Screen Intents?
Full-Screen Intents are Intents that can launch in full-screen and can be used for showing a full-screen notification.
Well, I guess that needs a bit more explanation so keep on reading.
From the official docs:
Your app might need to display an urgent, time-sensitive message, such as an incoming phone call or a ringing alarm. In these situations, you can associate a full-screen intent with your notification.
Why should Full-Screen Intents be used?
A restriction was added with Android Q where an app couldn’t start an activity if not meeting criteria. Although this breaks a lot of things, it doesn’t affect full-screen intents. (see Restrictions on starting activities from the background)
From Ian Lake from the Android Toolkit team at Stackoverflow:
Full screen intent has been the recommended best practice for alarms since it was introduced in API 9 and was even more important with the introduction of heads up notifications (where your alarm shows as a heads up notification if the user is actively using their device)
When should Full-Screen Intents be used?
Full-screen intents were added to the framework since forever and it is the recommended way of launching an activity while the system is locked or busy.
Examples:
- Incoming call: When there is an incoming call, the system launches a full-screen activity if the phone is locked or shows a normal notification with high priority.
- Alarm Clock: An alarm clock can use full-screen intent to either show an activity or a notification with high priority.
Notifications with a Full-Screen Intent are less intrusive to the user and there is less chance to break in the future with any API changes.
Show me the code
Note: On Android 10 and above to use a full-screen intent, a special permission needs to be declared in the Manifest.
We are going to explore three different scenarios:
- Notify while the app is on the foreground
- Schedule Full-Screen Intent Notification
- Full-Screen Intent on Lock Screen with a Keyguard
Disclaimer: Some logic will be emitted for demonstration purposes
1. Notify while the app on the foreground.
In order to show a full-screen intent, we need to first build the notification and set the full-screen intent to the notification.
To build the intent we need a pending intent, which can be achieved using PendingIntent.
val builder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(android.R.drawable.arrow_up_float)
.setContentTitle(title)
.setContentText(description)
.setPriority(NotificationCompat.PRIORITY_HIGH)
// request code and flags not added for demo purposes
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
builder.setFullScreenIntent(pendingIntent) // THIS HERE is the full-screen intent
Don’t forget to add in your AndroidManifest.xml’s Activity the following as well:
<activity android:name=".LockScreenActivity"
android:showOnLockScreen="true"/>
2. Schedule Full-Screen Intent Notification
Building the notification is identical to the previous example. The main difference is that the notification is not built by an Activity but by a BroadcastReceiver to enable scheduling in the future using AlarmManager.
Scheduling AlarmManager needs a PendingIntent with a BroadcastReceiver.
fun Context.scheduleNotification(isLockScreen: Boolean) {
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val timeInMillis = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(SCHEDULE_TIME)
with(alarmManager) {
setExact(AlarmManager.RTC_WAKEUP, timeInMillis, getReceiver(isLockScreen))
}
}
private fun Context.getReceiver(isLockScreen: Boolean): PendingIntent {
// for demo purposes no request code and no flags
return PendingIntent.getBroadcast(
this,
0,
NotificationReceiver.build(this, isLockScreen),
0
)
}
Receiver The Receiver below is called in two cases:
- When the system is locked
- When the system is not locked
class NotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if(intent.getBooleanExtra(LOCK_SCREEN_KEY, true)) {
context.showNotificationWithFullScreenIntent(true)
} else {
context.showNotificationWithFullScreenIntent()
}
}
companion object {
fun build(context: Context, isLockScreen: Boolean): Intent {
return Intent(context, NotificationReceiver::class.java).also {
it.putExtra(LOCK_SCREEN_KEY, isLockScreen)
}
}
}
}
private const val LOCK_SCREEN_KEY = "lockScreenKey"
3. Full-Screen Intent on Lock Screen with a Keyguard
Keyguard could prevent the notification from being displayed.
Let’s see an example to visualize it.
The framework provides some flags to dismiss keyguard.
Activity#setShowWhenLocked(true) method Note: To be able to turn on the screen we need to also request it using the:
– Activity#setTurnScreenOn(true) method
Time for an extension function that sets both flags with backward compatibility 🚀
fun Activity.turnScreenOnAndKeyguardOff() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
setTurnScreenOn(true)
} else {
window.addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
)
}
with(getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
requestDismissKeyguard(this@turnScreenOnAndKeyguardOff, null)
}
}
}
Demo 🎬
Conclusion
Using Full-Screen Intents is easy and it’s the recommended way of launching an activity, especially for alarms.
The API should be used with care, and developers shouldn’t abuse it as it’s quite intrusive especially when the screen wakes up.
You can find a sample project here.
Feel free to ping me on twitter.
Till next time! 👋