package com.peachpasswords.sync import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import androidx.core.app.NotificationCompat import com.peachpasswords.MainActivity import com.peachpasswords.R import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import javax.inject.Singleton @Singleton class SyncAlertStore @Inject constructor( @ApplicationContext private val context: Context, ) : SyncAlertRecorder { init { // v2.7.4 and earlier persisted security banners for the vault list. // They are intentionally not migrated into surprise notifications. context.deleteSharedPreferences(LEGACY_PREFS_FILE) } override fun record(alerts: List) { // Final display seam: cap whatever name reached the queue. val capped = alerts.map { it.copy(deviceName = it.deviceName?.let(::capDeviceName)) } val content = syncSecurityNotificationContent(capped) ?: return val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager runCatching { manager.notify( SYNC_SECURITY_NOTIFICATION_ID, buildSyncSecurityNotification(context, content), ) } } fun clear() { val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager manager.cancel(SYNC_SECURITY_NOTIFICATION_ID) } } internal const val SYNC_SECURITY_NOTIFICATION_CHANNEL_ID = "peach_sync_security" internal const val SYNC_SECURITY_NOTIFICATION_ID = 4202 private const val LEGACY_PREFS_FILE = "peach_sync_alerts" internal fun buildSyncSecurityNotification( context: Context, content: SyncSecurityNotificationContent, ): Notification { val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager manager.createNotificationChannel( NotificationChannel( SYNC_SECURITY_NOTIFICATION_CHANNEL_ID, "Peach Sync security", NotificationManager.IMPORTANCE_DEFAULT, ).apply { description = "Device removals and unexpected Peach Sync key changes." }, ) val open = PendingIntent.getActivity( context, 0, Intent(context, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, ) val builder = NotificationCompat.Builder(context, SYNC_SECURITY_NOTIFICATION_CHANNEL_ID) .setContentTitle(content.title) .setContentText(content.summary) .setSmallIcon(R.drawable.ic_launcher_monochrome) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setCategory(NotificationCompat.CATEGORY_STATUS) .setVisibility(NotificationCompat.VISIBILITY_PRIVATE) .setAutoCancel(true) .setContentIntent(open) if (content.lines.size == 1) { builder.setStyle(NotificationCompat.BigTextStyle().bigText(content.summary)) } else { builder.setStyle( NotificationCompat.InboxStyle() .also { style -> content.lines.takeLast(5).forEach(style::addLine) } .setSummaryText("${content.lines.size} device security updates"), ) } return builder.build() }