Skip to content

Commit 8f67b3c

Browse files
authored
fix: use full screen intent properly (#613)
* fix: add missing full screen notification permissions I have no idea why this wasn't added yet the strings for requesting this permission exist. * fix: drop manual check for interactive device * fix: rework when full-screen intent is used * docs: update changelog * refactor: simplify method * refactor: remove unused `isOreoPlus` import
1 parent 180719d commit 8f67b3c

File tree

7 files changed

+135
-48
lines changed

7 files changed

+135
-48
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
## [Unreleased]
88

99
### Changed
10-
1110
- Compatibility updates for Android 15 & 16
1211

12+
### Fixed
13+
- Fixed incoming call screen hidden by lock screen ([#165])
14+
1315
## [1.7.3] - 2025-10-16
1416
### Changed
1517
- Updated translations
@@ -203,6 +205,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
203205
[#139]: https://github.com/FossifyOrg/Phone/issues/139
204206
[#146]: https://github.com/FossifyOrg/Phone/issues/146
205207
[#147]: https://github.com/FossifyOrg/Phone/issues/147
208+
[#165]: https://github.com/FossifyOrg/Phone/issues/165
206209
[#181]: https://github.com/FossifyOrg/Phone/issues/181
207210
[#183]: https://github.com/FossifyOrg/Phone/issues/183
208211
[#186]: https://github.com/FossifyOrg/Phone/issues/186

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<uses-permission android:name="android.permission.VIBRATE" />
2222
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
2323
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
24+
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
2425

2526
<uses-permission
2627
android:name="android.permission.USE_FINGERPRINT"

app/src/main/kotlin/org/fossify/phone/activities/MainActivity.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import org.fossify.phone.dialogs.ChangeSortingDialog
3434
import org.fossify.phone.dialogs.FilterContactSourcesDialog
3535
import org.fossify.phone.extensions.clearMissedCalls
3636
import org.fossify.phone.extensions.config
37+
import org.fossify.phone.extensions.handleFullScreenNotificationsPermission
3738
import org.fossify.phone.extensions.launchCreateNewContactIntent
3839
import org.fossify.phone.fragments.ContactsFragment
3940
import org.fossify.phone.fragments.FavoritesFragment
@@ -73,7 +74,11 @@ class MainActivity : SimpleActivity() {
7374
checkContactPermissions()
7475

7576
if (!config.wasOverlaySnackbarConfirmed && !Settings.canDrawOverlays(this)) {
76-
val snackbar = Snackbar.make(binding.mainHolder, R.string.allow_displaying_over_other_apps, Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok) {
77+
val snackbar = Snackbar.make(
78+
binding.mainHolder,
79+
R.string.allow_displaying_over_other_apps,
80+
Snackbar.LENGTH_INDEFINITE
81+
).setAction(R.string.ok) {
7782
config.wasOverlaySnackbarConfirmed = true
7883
startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION))
7984
}
@@ -84,9 +89,9 @@ class MainActivity : SimpleActivity() {
8489
snackbar.show()
8590
}
8691

87-
handleNotificationPermission { granted ->
92+
handleFullScreenNotificationsPermission { granted ->
8893
if (!granted) {
89-
PermissionRequiredDialog(this, R.string.allow_notifications_incoming_calls, { openNotificationSettings() })
94+
toast(org.fossify.commons.R.string.notifications_disabled)
9095
}
9196
}
9297
} else {

app/src/main/kotlin/org/fossify/phone/extensions/Activity.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,27 @@ import android.provider.ContactsContract
88
import android.telecom.PhoneAccount
99
import android.telecom.PhoneAccountHandle
1010
import android.telecom.TelecomManager
11+
import org.fossify.commons.R
1112
import org.fossify.commons.activities.BaseSimpleActivity
1213
import org.fossify.commons.dialogs.CallConfirmationDialog
14+
import org.fossify.commons.dialogs.PermissionRequiredDialog
15+
import org.fossify.commons.extensions.canUseFullScreenIntent
1316
import org.fossify.commons.extensions.initiateCall
1417
import org.fossify.commons.extensions.isDefaultDialer
1518
import org.fossify.commons.extensions.isPackageInstalled
1619
import org.fossify.commons.extensions.launchActivityIntent
1720
import org.fossify.commons.extensions.launchCallIntent
1821
import org.fossify.commons.extensions.launchViewContactIntent
22+
import org.fossify.commons.extensions.openFullScreenIntentSettings
23+
import org.fossify.commons.extensions.openNotificationSettings
1924
import org.fossify.commons.extensions.telecomManager
2025
import org.fossify.commons.helpers.CONTACT_ID
2126
import org.fossify.commons.helpers.IS_PRIVATE
2227
import org.fossify.commons.helpers.PERMISSION_READ_PHONE_STATE
2328
import org.fossify.commons.helpers.SimpleContactsHelper
2429
import org.fossify.commons.helpers.ensureBackgroundThread
2530
import org.fossify.commons.models.contacts.Contact
31+
import org.fossify.phone.BuildConfig
2632
import org.fossify.phone.activities.DialerActivity
2733
import org.fossify.phone.activities.SimpleActivity
2834
import org.fossify.phone.dialogs.SelectSIMDialog
@@ -181,3 +187,36 @@ fun SimpleActivity.showSelectSimDialog(
181187
) { handle ->
182188
callback(handle)
183189
}
190+
191+
fun SimpleActivity.handleFullScreenNotificationsPermission(callback: (granted: Boolean) -> Unit) {
192+
handleNotificationPermission { granted ->
193+
if (granted) {
194+
if (canUseFullScreenIntent()) {
195+
callback(true)
196+
} else {
197+
PermissionRequiredDialog(
198+
activity = this,
199+
textId = R.string.allow_full_screen_notifications_incoming_calls,
200+
positiveActionCallback = {
201+
@SuppressLint("NewApi")
202+
openFullScreenIntentSettings(BuildConfig.APPLICATION_ID)
203+
},
204+
negativeActionCallback = {
205+
callback(false)
206+
}
207+
)
208+
}
209+
} else {
210+
PermissionRequiredDialog(
211+
activity = this,
212+
textId = R.string.allow_notifications_incoming_calls,
213+
positiveActionCallback = {
214+
openNotificationSettings()
215+
},
216+
negativeActionCallback = {
217+
callback(false)
218+
}
219+
)
220+
}
221+
}
222+
}

app/src/main/kotlin/org/fossify/phone/extensions/Context.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package org.fossify.phone.extensions
22

33
import android.annotation.SuppressLint
4+
import android.app.KeyguardManager
45
import android.content.Context
6+
import android.content.Context.KEYGUARD_SERVICE
57
import android.content.Intent
68
import android.media.AudioManager
79
import android.net.Uri
@@ -14,9 +16,14 @@ import org.fossify.phone.models.SIMAccount
1416

1517
val Context.config: Config get() = Config.newInstance(applicationContext)
1618

17-
val Context.audioManager: AudioManager get() = getSystemService(Context.AUDIO_SERVICE) as AudioManager
19+
val Context.audioManager: AudioManager
20+
get() = getSystemService(Context.AUDIO_SERVICE) as AudioManager
1821

19-
val Context.powerManager: PowerManager get() = getSystemService(Context.POWER_SERVICE) as PowerManager
22+
val Context.powerManager: PowerManager
23+
get() = getSystemService(Context.POWER_SERVICE) as PowerManager
24+
25+
val Context.keyguardManager: KeyguardManager
26+
get() = getSystemService(KEYGUARD_SERVICE) as KeyguardManager
2027

2128
@SuppressLint("MissingPermission")
2229
fun Context.getAvailableSIMCardLabels(): List<SIMAccount> {

app/src/main/kotlin/org/fossify/phone/helpers/CallNotificationManager.kt

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,64 +3,65 @@ package org.fossify.phone.helpers
33
import android.annotation.SuppressLint
44
import android.app.Notification
55
import android.app.NotificationChannel
6-
import android.app.NotificationManager
6+
import android.app.NotificationManager.IMPORTANCE_DEFAULT
7+
import android.app.NotificationManager.IMPORTANCE_HIGH
78
import android.app.PendingIntent
89
import android.content.Context
910
import android.content.Intent
1011
import android.telecom.Call
1112
import android.widget.RemoteViews
12-
import androidx.core.app.NotificationCompat
1313
import org.fossify.commons.extensions.notificationManager
1414
import org.fossify.commons.extensions.setText
1515
import org.fossify.commons.extensions.setVisibleIf
16-
import org.fossify.commons.helpers.isOreoPlus
1716
import org.fossify.phone.R
1817
import org.fossify.phone.activities.CallActivity
19-
import org.fossify.phone.extensions.powerManager
2018
import org.fossify.phone.receivers.CallActionReceiver
2119

2220
class CallNotificationManager(private val context: Context) {
23-
private val CALL_NOTIFICATION_ID = 42
24-
private val ACCEPT_CALL_CODE = 0
25-
private val DECLINE_CALL_CODE = 1
21+
companion object {
22+
private const val CALL_NOTIFICATION_ID = 42
23+
private const val ACCEPT_CALL_CODE = 0
24+
private const val DECLINE_CALL_CODE = 1
25+
}
26+
2627
private val notificationManager = context.notificationManager
2728
private val callContactAvatarHelper = CallContactAvatarHelper(context)
2829

2930
@SuppressLint("NewApi")
30-
fun setupNotification(forceLowPriority: Boolean = false) {
31+
fun setupNotification(lowPriority: Boolean = false) {
3132
getCallContact(context.applicationContext, CallManager.getPrimaryCall()) { callContact ->
3233
val callContactAvatar = callContactAvatarHelper.getCallContactAvatar(callContact)
3334
val callState = CallManager.getState()
34-
val isHighPriority = context.powerManager.isInteractive && callState == Call.STATE_RINGING && !forceLowPriority
35-
val channelId = if (isHighPriority) "simple_dialer_call_high_priority" else "simple_dialer_call"
36-
if (isOreoPlus()) {
37-
val importance = if (isHighPriority) NotificationManager.IMPORTANCE_HIGH else NotificationManager.IMPORTANCE_DEFAULT
38-
val name = if (isHighPriority) {
39-
context.getString(R.string.call_notification_channel_high_priority)
40-
} else {
41-
context.getString(R.string.call_notification_channel)
42-
}
43-
44-
NotificationChannel(channelId, name, importance).apply {
45-
setSound(null, null)
46-
notificationManager.createNotificationChannel(this)
47-
}
48-
}
35+
val isHighPriority = callState == Call.STATE_RINGING && !lowPriority
36+
val channelId =
37+
if (isHighPriority) "simple_dialer_call_high_priority" else "simple_dialer_call"
38+
createNotificationChannel(isHighPriority, channelId)
4939

5040
val openAppIntent = CallActivity.getStartIntent(context)
51-
val openAppPendingIntent = PendingIntent.getActivity(context, 0, openAppIntent, PendingIntent.FLAG_MUTABLE)
41+
val openAppPendingIntent =
42+
PendingIntent.getActivity(context, 0, openAppIntent, PendingIntent.FLAG_MUTABLE)
5243

5344
val acceptCallIntent = Intent(context, CallActionReceiver::class.java)
5445
acceptCallIntent.action = ACCEPT_CALL
5546
val acceptPendingIntent =
56-
PendingIntent.getBroadcast(context, ACCEPT_CALL_CODE, acceptCallIntent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE)
47+
PendingIntent.getBroadcast(
48+
context,
49+
ACCEPT_CALL_CODE,
50+
acceptCallIntent,
51+
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE
52+
)
5753

5854
val declineCallIntent = Intent(context, CallActionReceiver::class.java)
5955
declineCallIntent.action = DECLINE_CALL
6056
val declinePendingIntent =
61-
PendingIntent.getBroadcast(context, DECLINE_CALL_CODE, declineCallIntent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE)
62-
63-
var callerName = if (callContact.name.isNotEmpty()) callContact.name else context.getString(R.string.unknown_caller)
57+
PendingIntent.getBroadcast(
58+
context,
59+
DECLINE_CALL_CODE,
60+
declineCallIntent,
61+
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE
62+
)
63+
64+
var callerName = callContact.name.ifEmpty { context.getString(R.string.unknown_caller) }
6465
if (callContact.numberLabel.isNotEmpty()) {
6566
callerName += " - ${callContact.numberLabel}"
6667
}
@@ -82,21 +83,22 @@ class CallNotificationManager(private val context: Context) {
8283
setOnClickPendingIntent(R.id.notification_accept_call, acceptPendingIntent)
8384

8485
if (callContactAvatar != null) {
85-
setImageViewBitmap(R.id.notification_thumbnail, callContactAvatarHelper.getCircularBitmap(callContactAvatar))
86+
setImageViewBitmap(
87+
R.id.notification_thumbnail,
88+
callContactAvatarHelper.getCircularBitmap(callContactAvatar)
89+
)
8690
}
8791
}
8892

89-
val builder = NotificationCompat.Builder(context, channelId)
93+
val builder = Notification.Builder(context, channelId)
9094
.setSmallIcon(R.drawable.ic_phone_vector)
9195
.setContentIntent(openAppPendingIntent)
92-
.setPriority(if (isHighPriority) NotificationManager.IMPORTANCE_HIGH else NotificationCompat.PRIORITY_DEFAULT)
9396
.setCategory(Notification.CATEGORY_CALL)
9497
.setCustomContentView(collapsedView)
9598
.setOngoing(true)
96-
.setSound(null)
9799
.setUsesChronometer(callState == Call.STATE_ACTIVE)
98100
.setChannelId(channelId)
99-
.setStyle(NotificationCompat.DecoratedCustomViewStyle())
101+
.setStyle(Notification.DecoratedCustomViewStyle())
100102

101103
if (isHighPriority) {
102104
builder.setFullScreenIntent(openAppPendingIntent, true)
@@ -110,6 +112,20 @@ class CallNotificationManager(private val context: Context) {
110112
}
111113
}
112114

115+
fun createNotificationChannel(isHighPriority: Boolean, channelId: String) {
116+
val name = if (isHighPriority) {
117+
context.getString(R.string.call_notification_channel_high_priority)
118+
} else {
119+
context.getString(R.string.call_notification_channel)
120+
}
121+
122+
val importance = if (isHighPriority) IMPORTANCE_HIGH else IMPORTANCE_DEFAULT
123+
NotificationChannel(channelId, name, importance).apply {
124+
setSound(null, null)
125+
notificationManager.createNotificationChannel(this)
126+
}
127+
}
128+
113129
fun cancelNotification() {
114130
notificationManager.cancel(CALL_NOTIFICATION_ID)
115131
}

app/src/main/kotlin/org/fossify/phone/services/CallService.kt

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package org.fossify.phone.services
22

3-
import android.app.KeyguardManager
4-
import android.content.Context
53
import android.telecom.Call
64
import android.telecom.CallAudioState
75
import android.telecom.InCallService
6+
import org.fossify.commons.extensions.canUseFullScreenIntent
7+
import org.fossify.commons.extensions.hasPermission
8+
import org.fossify.commons.helpers.PERMISSION_POST_NOTIFICATIONS
89
import org.fossify.phone.activities.CallActivity
910
import org.fossify.phone.extensions.config
1011
import org.fossify.phone.extensions.isOutgoing
12+
import org.fossify.phone.extensions.keyguardManager
1113
import org.fossify.phone.extensions.powerManager
1214
import org.fossify.phone.helpers.CallManager
1315
import org.fossify.phone.helpers.CallNotificationManager
@@ -35,17 +37,31 @@ class CallService : InCallService() {
3537
CallManager.inCallService = this
3638
call.registerCallback(callListener)
3739

38-
val isScreenLocked = (getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager).isDeviceLocked
39-
if (!powerManager.isInteractive || call.isOutgoing() || isScreenLocked || config.alwaysShowFullscreen) {
40+
// Incoming/Outgoing (locked): high priority (FSI)
41+
// Incoming (unlocked): if user opted in, low priority ➜ manual activity start, otherwise high priority (FSI)
42+
// Outgoing (unlocked): low priority ➜ manual activity start
43+
val isIncoming = !call.isOutgoing()
44+
val isDeviceLocked = !powerManager.isInteractive || keyguardManager.isDeviceLocked
45+
val lowPriority = when {
46+
isIncoming && isDeviceLocked -> false
47+
!isIncoming && isDeviceLocked -> false
48+
isIncoming && !isDeviceLocked -> config.alwaysShowFullscreen
49+
else -> true
50+
}
51+
52+
callNotificationManager.setupNotification(lowPriority)
53+
if (
54+
lowPriority
55+
|| !hasPermission(PERMISSION_POST_NOTIFICATIONS)
56+
|| !canUseFullScreenIntent()
57+
) {
4058
try {
41-
callNotificationManager.setupNotification(true)
4259
startActivity(CallActivity.getStartIntent(this))
43-
} catch (e: Exception) {
44-
// seems like startActivity can throw AndroidRuntimeException and ActivityNotFoundException, not yet sure when and why, lets show a notification
60+
} catch (_: Exception) {
61+
// seems like startActivity can throw AndroidRuntimeException and
62+
// ActivityNotFoundException, not yet sure when and why, lets show a notification
4563
callNotificationManager.setupNotification()
4664
}
47-
} else {
48-
callNotificationManager.setupNotification()
4965
}
5066
}
5167

0 commit comments

Comments
 (0)