/* * Copyright (C) 2023 MURENA SAS * Copyright (C) 2022 MURENA SAS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package foundation.e.advancedprivacy import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import androidx.annotation.StringRes import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode import foundation.e.advancedprivacy.domain.entities.MainFeatures import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase import foundation.e.advancedprivacy.main.MainActivity import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach object Notifications { const val CHANNEL_FIRST_BOOT = "first_boot_notification" const val CHANNEL_FAKE_LOCATION_FLAG = "fake_location_flag" const val CHANNEL_IPSCRAMBLING_FLAG = "ipscrambling_flag" const val NOTIFICATION_FIRST_BOOT = 1000 const val NOTIFICATION_FAKE_LOCATION_FLAG = NOTIFICATION_FIRST_BOOT + 1 const val NOTIFICATION_IPSCRAMBLING_FLAG = NOTIFICATION_FAKE_LOCATION_FLAG + 1 fun showFirstBootNotification(context: Context) { createNotificationFirstBootChannel(context) val notificationBuilder: NotificationCompat.Builder = notificationBuilder( context, NotificationContent( channelId = CHANNEL_FIRST_BOOT, icon = R.drawable.ic_notification_logo, title = R.string.first_notification_title, description = R.string.first_notification_summary, pendingIntent = MainActivity.deepLinkBuilder(context) .setDestination(R.id.dashboardFragment) .createPendingIntent() ) ) .setAutoCancel(true) NotificationManagerCompat.from(context).notify( NOTIFICATION_FIRST_BOOT, notificationBuilder.build() ) } fun startListening( appContext: Context, getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase, permissionsPrivacyModule: PermissionsPrivacyModule, appScope: CoroutineScope ) { createNotificationFlagChannel( context = appContext, permissionsPrivacyModule = permissionsPrivacyModule, channelId = CHANNEL_FAKE_LOCATION_FLAG, channelName = R.string.notifications_fake_location_channel_name, channelDescription = R.string.notifications_fake_location_channel_description ) createNotificationFlagChannel( context = appContext, permissionsPrivacyModule = permissionsPrivacyModule, channelId = CHANNEL_IPSCRAMBLING_FLAG, channelName = R.string.notifications_ipscrambling_channel_name, channelDescription = R.string.notifications_ipscrambling_channel_description ) getQuickPrivacyStateUseCase.isLocationHidden.onEach { if (it) { showFlagNotification(appContext, MainFeatures.FAKE_LOCATION) } else { hideFlagNotification(appContext, MainFeatures.FAKE_LOCATION) } }.launchIn(appScope) getQuickPrivacyStateUseCase.ipScramblingMode.map { it != InternetPrivacyMode.REAL_IP }.distinctUntilChanged().onEach { if (it) { showFlagNotification(appContext, MainFeatures.IP_SCRAMBLING) } else { hideFlagNotification(appContext, MainFeatures.IP_SCRAMBLING) } }.launchIn(appScope) } private fun createNotificationFirstBootChannel(context: Context) { val channel = NotificationChannel( CHANNEL_FIRST_BOOT, context.getString(R.string.notifications_first_boot_channel_name), NotificationManager.IMPORTANCE_HIGH ) NotificationManagerCompat.from(context).createNotificationChannel(channel) } private fun createNotificationFlagChannel( context: Context, permissionsPrivacyModule: PermissionsPrivacyModule, channelId: String, @StringRes channelName: Int, @StringRes channelDescription: Int, ) { val channel = NotificationChannel( channelId, context.getString(channelName), NotificationManager.IMPORTANCE_LOW ) channel.description = context.getString(channelDescription) permissionsPrivacyModule.setBlockable(channel) NotificationManagerCompat.from(context).createNotificationChannel(channel) } private fun showFlagNotification(context: Context, feature: MainFeatures) { when (feature) { MainFeatures.FAKE_LOCATION -> showFlagNotification( context = context, id = NOTIFICATION_FAKE_LOCATION_FLAG, content = NotificationContent( channelId = CHANNEL_FAKE_LOCATION_FLAG, icon = R.drawable.ic_fmd_bad, title = R.string.notifications_fake_location_title, description = R.string.notifications_fake_location_content, pendingIntent = MainActivity.deepLinkBuilder(context) .addDestination(R.id.fakeLocationFragment) .createPendingIntent() ) ) MainFeatures.IP_SCRAMBLING -> showFlagNotification( context = context, id = NOTIFICATION_IPSCRAMBLING_FLAG, content = NotificationContent( channelId = CHANNEL_IPSCRAMBLING_FLAG, icon = R.drawable.ic_language, title = R.string.notifications_ipscrambling_title, description = R.string.notifications_ipscrambling_content, pendingIntent = MainActivity.deepLinkBuilder(context) .addDestination(R.id.internetPrivacyFragment) .createPendingIntent() ) ) else -> {} } } private fun showFlagNotification( context: Context, id: Int, content: NotificationContent, ) { val builder = notificationBuilder(context, content) .setPriority(NotificationCompat.PRIORITY_LOW) .setOngoing(true) NotificationManagerCompat.from(context).notify(id, builder.build()) } private fun hideFlagNotification(context: Context, feature: MainFeatures) { val id = when (feature) { MainFeatures.FAKE_LOCATION -> NOTIFICATION_FAKE_LOCATION_FLAG MainFeatures.IP_SCRAMBLING -> NOTIFICATION_IPSCRAMBLING_FLAG else -> return } NotificationManagerCompat.from(context).cancel(id) } private data class NotificationContent( val channelId: String, val icon: Int, val title: Int, val description: Int, val pendingIntent: PendingIntent? ) private fun notificationBuilder( context: Context, content: NotificationContent ): NotificationCompat.Builder { val builder = NotificationCompat.Builder(context, content.channelId) .setSmallIcon(content.icon) .setPriority(NotificationCompat.PRIORITY_LOW) .setContentTitle(context.getString(content.title)) .setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(content.description))) content.pendingIntent?.let { builder.setContentIntent(it) } return builder } }