PIN: Add a setting to hide notification content when PIN code is configured

This commit is contained in:
Benoit Marty 2020-09-23 15:57:20 +02:00 committed by Benoit Marty
parent a6cf2b0685
commit 44b2673848
7 changed files with 136 additions and 25 deletions

View file

@ -12,7 +12,7 @@ Improvements 🙌:
- Use cache for user color - Use cache for user color
- Allow using an outdated homeserver, at user's risk (#1972) - Allow using an outdated homeserver, at user's risk (#1972)
- Restore small logo on login screens and fix scrolling issue on those screens - Restore small logo on login screens and fix scrolling issue on those screens
- PIN Code Improvements. Add more settings (#1985) - PIN Code Improvements: Add more settings: biometrics, grace period, notification content (#1985)
Bugfix 🐛: Bugfix 🐛:
- Long message cannot be sent/takes infinite time & blocks other messages #1397 - Long message cannot be sent/takes infinite time & blocks other messages #1397

View file

@ -27,9 +27,9 @@ import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import me.gujun.android.span.span
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.content.ContentUrlResolver
import me.gujun.android.span.span
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@ -72,6 +72,8 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
private val currentSession: Session? private val currentSession: Session?
get() = activeSessionDataSource.currentValue?.orNull() get() = activeSessionDataSource.currentValue?.orNull()
private var useCompleteNotificationFormat = vectorPreferences.useCompleteNotificationFormat()
/** /**
Should be called as soon as a new event is ready to be displayed. Should be called as soon as a new event is ready to be displayed.
The notification corresponding to this event will not be displayed until The notification corresponding to this event will not be displayed until
@ -243,8 +245,8 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
roomEvents.add(event) roomEvents.add(event)
} }
} }
is InviteNotifiableEvent -> invitationEvents.add(event) is InviteNotifiableEvent -> invitationEvents.add(event)
is SimpleNotifiableEvent -> simpleEvents.add(event) is SimpleNotifiableEvent -> simpleEvents.add(event)
else -> Timber.w("Type not handled") else -> Timber.w("Type not handled")
} }
} }
@ -253,6 +255,16 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
var globalLastMessageTimestamp = 0L var globalLastMessageTimestamp = 0L
val newSettings = vectorPreferences.useCompleteNotificationFormat()
if (newSettings != useCompleteNotificationFormat) {
// Settings has changed, remove all current notifications
notificationUtils.cancelAllNotifications()
useCompleteNotificationFormat = newSettings
}
var simpleNotificationRoomCounter = 0
var simpleNotificationMessageCounter = 0
// events have been grouped by roomId // events have been grouped by roomId
for ((roomId, events) in roomIdToEventMap) { for ((roomId, events) in roomIdToEventMap) {
// Build the notification for the room // Build the notification for the room
@ -263,6 +275,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
continue continue
} }
simpleNotificationRoomCounter++
val roomName = events[0].roomName ?: events[0].senderName ?: "" val roomName = events[0].roomName ?: events[0].senderName ?: ""
val roomEventGroupInfo = RoomEventGroupInfo( val roomEventGroupInfo = RoomEventGroupInfo(
@ -303,6 +316,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
roomEventGroupInfo.hasSmartReplyError = true roomEventGroupInfo.hasSmartReplyError = true
} else { } else {
if (!event.isRedacted) { if (!event.isRedacted) {
simpleNotificationMessageCounter++
style.addMessage(event.body, event.timestamp, senderPerson) style.addMessage(event.body, event.timestamp, senderPerson)
} }
} }
@ -361,16 +375,18 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
stringProvider.getString(R.string.notification_ticker_text_group, roomName, events.last().senderName, events.last().description) stringProvider.getString(R.string.notification_ticker_text_group, roomName, events.last().senderName, events.last().description)
} }
val notification = notificationUtils.buildMessagesListNotification( if (useCompleteNotificationFormat) {
style, val notification = notificationUtils.buildMessagesListNotification(
roomEventGroupInfo, style,
largeBitmap, roomEventGroupInfo,
lastMessageTimestamp, largeBitmap,
myUserDisplayName, lastMessageTimestamp,
tickerText) myUserDisplayName,
tickerText)
// is there an id for this room? // is there an id for this room?
notificationUtils.showNotificationMessage(roomId, ROOM_MESSAGES_NOTIFICATION_ID, notification) notificationUtils.showNotificationMessage(roomId, ROOM_MESSAGES_NOTIFICATION_ID, notification)
}
hasNewEvent = true hasNewEvent = true
summaryIsNoisy = summaryIsNoisy || roomEventGroupInfo.shouldBing summaryIsNoisy = summaryIsNoisy || roomEventGroupInfo.shouldBing
@ -383,8 +399,10 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
for (event in invitationEvents) { for (event in invitationEvents) {
// We build a invitation notification // We build a invitation notification
if (firstTime || !event.hasBeenDisplayed) { if (firstTime || !event.hasBeenDisplayed) {
val notification = notificationUtils.buildRoomInvitationNotification(event, session.myUserId) if (useCompleteNotificationFormat) {
notificationUtils.showNotificationMessage(event.roomId, ROOM_INVITATION_NOTIFICATION_ID, notification) val notification = notificationUtils.buildRoomInvitationNotification(event, session.myUserId)
notificationUtils.showNotificationMessage(event.roomId, ROOM_INVITATION_NOTIFICATION_ID, notification)
}
event.hasBeenDisplayed = true // we can consider it as displayed event.hasBeenDisplayed = true // we can consider it as displayed
hasNewEvent = true hasNewEvent = true
summaryIsNoisy = summaryIsNoisy || event.noisy summaryIsNoisy = summaryIsNoisy || event.noisy
@ -396,8 +414,10 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
for (event in simpleEvents) { for (event in simpleEvents) {
// We build a simple notification // We build a simple notification
if (firstTime || !event.hasBeenDisplayed) { if (firstTime || !event.hasBeenDisplayed) {
val notification = notificationUtils.buildSimpleEventNotification(event, session.myUserId) if (useCompleteNotificationFormat) {
notificationUtils.showNotificationMessage(event.eventId, ROOM_EVENT_NOTIFICATION_ID, notification) val notification = notificationUtils.buildSimpleEventNotification(event, session.myUserId)
notificationUtils.showNotificationMessage(event.eventId, ROOM_EVENT_NOTIFICATION_ID, notification)
}
event.hasBeenDisplayed = true // we can consider it as displayed event.hasBeenDisplayed = true // we can consider it as displayed
hasNewEvent = true hasNewEvent = true
summaryIsNoisy = summaryIsNoisy || event.noisy summaryIsNoisy = summaryIsNoisy || event.noisy
@ -421,19 +441,72 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
if (eventList.isEmpty() || eventList.all { it.isRedacted }) { if (eventList.isEmpty() || eventList.all { it.isRedacted }) {
notificationUtils.cancelNotificationMessage(null, SUMMARY_NOTIFICATION_ID) notificationUtils.cancelNotificationMessage(null, SUMMARY_NOTIFICATION_ID)
} else { } else {
// FIXME roomIdToEventMap.size is not correct, this is the number of rooms
val nbEvents = roomIdToEventMap.size + simpleEvents.size val nbEvents = roomIdToEventMap.size + simpleEvents.size
val sumTitle = stringProvider.getQuantityString(R.plurals.notification_compat_summary_title, nbEvents, nbEvents) val sumTitle = stringProvider.getQuantityString(R.plurals.notification_compat_summary_title, nbEvents, nbEvents)
summaryInboxStyle.setBigContentTitle(sumTitle) summaryInboxStyle.setBigContentTitle(sumTitle)
// TODO get latest event? // TODO get latest event?
.setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents)) .setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents))
val notification = notificationUtils.buildSummaryListNotification( if (useCompleteNotificationFormat) {
summaryInboxStyle, val notification = notificationUtils.buildSummaryListNotification(
sumTitle, summaryInboxStyle,
noisy = hasNewEvent && summaryIsNoisy, sumTitle,
lastMessageTimestamp = globalLastMessageTimestamp) noisy = hasNewEvent && summaryIsNoisy,
lastMessageTimestamp = globalLastMessageTimestamp)
notificationUtils.showNotificationMessage(null, SUMMARY_NOTIFICATION_ID, notification) notificationUtils.showNotificationMessage(null, SUMMARY_NOTIFICATION_ID, notification)
} else {
// Add the simple events as message (?)
simpleNotificationMessageCounter += simpleEvents.size
val numberOfInvitations = invitationEvents.size
val privacyTitle = if (numberOfInvitations > 0) {
val invitationsStr = stringProvider.getQuantityString(R.plurals.notification_invitations, numberOfInvitations, numberOfInvitations)
if (simpleNotificationMessageCounter > 0) {
// Invitation and message
val messageStr = stringProvider.getQuantityString(R.plurals.room_new_messages_notification, simpleNotificationMessageCounter, simpleNotificationMessageCounter)
if (simpleNotificationRoomCounter > 1) {
// In several rooms
val roomStr = stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages_in_room_rooms, simpleNotificationRoomCounter, simpleNotificationRoomCounter)
stringProvider.getString(
R.string.notification_unread_notified_messages_in_room_and_invitation,
messageStr,
roomStr,
invitationsStr
)
} else {
// In one room
stringProvider.getString(
R.string.notification_unread_notified_messages_and_invitation,
messageStr,
invitationsStr
)
}
} else {
// Only invitation
invitationsStr
}
} else {
// No invitation, only messages
val messageStr = stringProvider.getQuantityString(R.plurals.room_new_messages_notification, simpleNotificationMessageCounter, simpleNotificationMessageCounter)
if (simpleNotificationRoomCounter > 1) {
// In several rooms
val roomStr = stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages_in_room_rooms, simpleNotificationMessageCounter, simpleNotificationMessageCounter)
stringProvider.getString(R.string.notification_unread_notified_messages_in_room, messageStr, roomStr)
} else {
// In one room
messageStr
}
}
val notification = notificationUtils.buildSummaryListNotification(
style = null,
compatSummary = privacyTitle,
noisy = hasNewEvent && summaryIsNoisy,
lastMessageTimestamp = globalLastMessageTimestamp)
notificationUtils.showNotificationMessage(null, SUMMARY_NOTIFICATION_ID, notification)
}
if (hasNewEvent && summaryIsNoisy) { if (hasNewEvent && summaryIsNoisy) {
try { try {

View file

@ -772,7 +772,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
/** /**
* Build the summary notification * Build the summary notification
*/ */
fun buildSummaryListNotification(style: NotificationCompat.InboxStyle, fun buildSummaryListNotification(style: NotificationCompat.InboxStyle?,
compatSummary: String, compatSummary: String,
noisy: Boolean, noisy: Boolean,
lastMessageTimestamp: Long): Notification { lastMessageTimestamp: Long): Notification {

View file

@ -168,6 +168,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
const val SETTINGS_SECURITY_USE_PIN_CODE_FLAG = "SETTINGS_SECURITY_USE_PIN_CODE_FLAG" const val SETTINGS_SECURITY_USE_PIN_CODE_FLAG = "SETTINGS_SECURITY_USE_PIN_CODE_FLAG"
private const val SETTINGS_SECURITY_USE_BIOMETRICS_FLAG = "SETTINGS_SECURITY_USE_BIOMETRICS_FLAG" private const val SETTINGS_SECURITY_USE_BIOMETRICS_FLAG = "SETTINGS_SECURITY_USE_BIOMETRICS_FLAG"
private const val SETTINGS_SECURITY_USE_GRACE_PERIOD_FLAG = "SETTINGS_SECURITY_USE_GRACE_PERIOD_FLAG" private const val SETTINGS_SECURITY_USE_GRACE_PERIOD_FLAG = "SETTINGS_SECURITY_USE_GRACE_PERIOD_FLAG"
const val SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG = "SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG"
// other // other
const val SETTINGS_MEDIA_SAVING_PERIOD_KEY = "SETTINGS_MEDIA_SAVING_PERIOD_KEY" const val SETTINGS_MEDIA_SAVING_PERIOD_KEY = "SETTINGS_MEDIA_SAVING_PERIOD_KEY"
@ -856,6 +857,14 @@ class VectorPreferences @Inject constructor(private val context: Context) {
return defaultPrefs.getBoolean(SETTINGS_SECURITY_USE_GRACE_PERIOD_FLAG, true) return defaultPrefs.getBoolean(SETTINGS_SECURITY_USE_GRACE_PERIOD_FLAG, true)
} }
/**
* Return true if Pin code is disabled, or if user set the settings to see full notification content
*/
fun useCompleteNotificationFormat(): Boolean {
return !useFlagPinCode()
|| defaultPrefs.getBoolean(SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG, true)
}
fun backgroundSyncTimeOut(): Int { fun backgroundSyncTimeOut(): Int {
return tryOrNull { return tryOrNull {
// The xml pref is saved as a string so use getString and parse // The xml pref is saved as a string so use getString and parse

View file

@ -22,6 +22,7 @@ import androidx.preference.Preference
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import im.vector.app.R import im.vector.app.R
import im.vector.app.features.navigation.Navigator import im.vector.app.features.navigation.Navigator
import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinActivity
import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinCodeStore
import im.vector.app.features.pin.PinMode import im.vector.app.features.pin.PinMode
@ -30,7 +31,8 @@ import javax.inject.Inject
class VectorSettingsPinFragment @Inject constructor( class VectorSettingsPinFragment @Inject constructor(
private val pinCodeStore: PinCodeStore, private val pinCodeStore: PinCodeStore,
private val navigator: Navigator private val navigator: Navigator,
private val notificationDrawerManager: NotificationDrawerManager
) : VectorSettingsBaseFragment() { ) : VectorSettingsBaseFragment() {
override var titleRes = R.string.settings_security_application_protection_screen_title override var titleRes = R.string.settings_security_application_protection_screen_title
@ -40,8 +42,18 @@ class VectorSettingsPinFragment @Inject constructor(
findPreference<SwitchPreference>(VectorPreferences.SETTINGS_SECURITY_USE_PIN_CODE_FLAG)!! findPreference<SwitchPreference>(VectorPreferences.SETTINGS_SECURITY_USE_PIN_CODE_FLAG)!!
} }
private val useCompleteNotificationPref by lazy {
findPreference<SwitchPreference>(VectorPreferences.SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG)!!
}
override fun bindPref() { override fun bindPref() {
refreshPinCodeStatus() refreshPinCodeStatus()
useCompleteNotificationPref.setOnPreferenceChangeListener { _, _ ->
// Refresh the drawer for an immediate effect of this change
notificationDrawerManager.refreshNotificationDrawer()
true
}
} }
private fun refreshPinCodeStatus() { private fun refreshPinCodeStatus() {

View file

@ -1154,6 +1154,10 @@
<item quantity="one">1 room</item> <item quantity="one">1 room</item>
<item quantity="other">%d rooms</item> <item quantity="other">%d rooms</item>
</plurals> </plurals>
<plurals name="notification_invitations">
<item quantity="one">1 invitation</item>
<item quantity="other">%d invitations</item>
</plurals>
<plurals name="notification_compat_summary_line_for_room"> <plurals name="notification_compat_summary_line_for_room">
<item quantity="one">%1$s: 1 message</item> <item quantity="one">%1$s: 1 message</item>
@ -1165,6 +1169,8 @@
</plurals> </plurals>
<string name="notification_unread_notified_messages_in_room">%1$s in %2$s"</string> <string name="notification_unread_notified_messages_in_room">%1$s in %2$s"</string>
<string name="notification_unread_notified_messages_in_room_and_invitation">%1$s in %2$s and %3$s"</string>
<string name="notification_unread_notified_messages_and_invitation">%1$s and %2$s"</string>
<string name="notification_unknown_new_event">New Event</string> <string name="notification_unknown_new_event">New Event</string>
<string name="notification_unknown_room_name">Room</string> <string name="notification_unknown_room_name">Room</string>
<string name="notification_new_messages">New Messages</string> <string name="notification_new_messages">New Messages</string>
@ -2574,6 +2580,9 @@
<string name="settings_security_pin_code_use_biometrics_title">Use biometrics</string> <string name="settings_security_pin_code_use_biometrics_title">Use biometrics</string>
<string name="settings_security_pin_code_use_biometrics_summary_on">Depending on your device, you will be able to unlock using fingerprints, face recognition, iris recognition, etc.</string> <string name="settings_security_pin_code_use_biometrics_summary_on">Depending on your device, you will be able to unlock using fingerprints, face recognition, iris recognition, etc.</string>
<string name="settings_security_pin_code_use_biometrics_summary_off">PIN code is the only way to unlock the application.</string> <string name="settings_security_pin_code_use_biometrics_summary_off">PIN code is the only way to unlock the application.</string>
<string name="settings_security_pin_code_notifications_title">Display complete notifications</string>
<string name="settings_security_pin_code_notifications_summary_on">Show full notification details, including rooms details and message contents.</string>
<string name="settings_security_pin_code_notifications_summary_off">Only display number of unread messages in a simple notification.</string>
<string name="settings_security_pin_code_grace_period_title">Enable grace period</string> <string name="settings_security_pin_code_grace_period_title">Enable grace period</string>
<string name="settings_security_pin_code_grace_period_summary_on">PIN code will be asked after 2 minutes not using the application.</string> <string name="settings_security_pin_code_grace_period_summary_on">PIN code will be asked after 2 minutes not using the application.</string>
<string name="settings_security_pin_code_grace_period_summary_off">PIN code will be requested every time you put the application to foreground.</string> <string name="settings_security_pin_code_grace_period_summary_off">PIN code will be requested every time you put the application to foreground.</string>

View file

@ -15,6 +15,14 @@
android:summaryOn="@string/settings_security_pin_code_use_biometrics_summary_on" android:summaryOn="@string/settings_security_pin_code_use_biometrics_summary_on"
android:title="@string/settings_security_pin_code_use_biometrics_title" /> android:title="@string/settings_security_pin_code_use_biometrics_title" />
<im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="true"
android:dependency="SETTINGS_SECURITY_USE_PIN_CODE_FLAG"
android:key="SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG"
android:summaryOff="@string/settings_security_pin_code_notifications_summary_off"
android:summaryOn="@string/settings_security_pin_code_notifications_summary_on"
android:title="@string/settings_security_pin_code_notifications_title" />
<im.vector.app.core.preference.VectorSwitchPreference <im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="true" android:defaultValue="true"
android:dependency="SETTINGS_SECURITY_USE_PIN_CODE_FLAG" android:dependency="SETTINGS_SECURITY_USE_PIN_CODE_FLAG"