summaryrefslogtreecommitdiff
path: root/app/src/main/java/foundation/e/privacycentralapp
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/foundation/e/privacycentralapp')
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt8
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/Notifications.kt210
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/common/BootCompletedReceiver.kt41
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt22
4 files changed, 242 insertions, 39 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
index f241e67..e6d4c42 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
@@ -143,9 +143,17 @@ class DependencyContainer(val app: Application) {
getQuickPrivacyStateUseCase,
trackersStatisticsUseCase,
)
+
+ Notifications.startListening(
+ context,
+ getQuickPrivacyStateUseCase,
+ permissionsModule,
+ GlobalScope
+ )
}
}
+@Suppress("LongParameterList")
class ViewModelsFactory(
private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
private val trackersStatisticsUseCase: TrackersStatisticsUseCase,
diff --git a/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt b/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt
new file mode 100644
index 0000000..0df3e18
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt
@@ -0,0 +1,210 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import androidx.annotation.StringRes
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode
+import foundation.e.privacycentralapp.domain.entities.MainFeatures
+import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
+import foundation.e.privacycentralapp.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,
+ destinationIntent =
+ context.packageManager.getLaunchIntentForPackage(context.packageName)
+ )
+ )
+ .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,
+ destinationIntent = MainActivity.createFakeLocationIntent(context),
+ )
+ )
+ 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,
+ destinationIntent = MainActivity.createIpScramblingIntent(context),
+ )
+ )
+ 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 destinationIntent: Intent?
+ )
+
+ 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.destinationIntent?.let {
+ it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ val pendingIntent: PendingIntent = PendingIntent.getActivity(
+ context, 0, it, PendingIntent.FLAG_IMMUTABLE
+ )
+ builder.setContentIntent(pendingIntent)
+ }
+
+ return builder
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/BootCompletedReceiver.kt b/app/src/main/java/foundation/e/privacycentralapp/common/BootCompletedReceiver.kt
index f43c2cc..d7902ee 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/common/BootCompletedReceiver.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/common/BootCompletedReceiver.kt
@@ -17,57 +17,20 @@
package foundation.e.privacycentralapp.common
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import androidx.core.app.NotificationCompat
-import foundation.e.privacycentralapp.R
+import foundation.e.privacycentralapp.Notifications
import foundation.e.privacycentralapp.data.repositories.LocalStateRepository
class BootCompletedReceiver : BroadcastReceiver() {
- companion object {
- const val FIRST_BOOT_NOTIFICATION_ID = 10
- }
-
override fun onReceive(context: Context, intent: Intent?) {
if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
val localStateRepository = LocalStateRepository(context)
if (localStateRepository.firstBoot) {
- showNotification(context)
+ Notifications.showFirstBootNotification(context)
localStateRepository.firstBoot = false
}
}
}
-
- private fun showNotification(context: Context) {
- val channelId = "first_boot_notification"
- val pendingIntent =
- PendingIntent.getActivity(
- context,
- 0,
- context.packageManager.getLaunchIntentForPackage(context.packageName),
- PendingIntent.FLAG_IMMUTABLE
- )
- val notificationBuilder: NotificationCompat.Builder =
- NotificationCompat.Builder(context, channelId)
- .setSmallIcon(R.drawable.ic_notification_logo)
- .setContentTitle(context.getString(R.string.first_notification_title))
- .setAutoCancel(true)
- .setContentIntent(pendingIntent)
- .setStyle(
- NotificationCompat.BigTextStyle()
- .bigText(context.getString(R.string.first_notification_summary))
- )
- val notificationManager =
- context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
-
- val name: CharSequence = "First Boot"
- val importance = NotificationManager.IMPORTANCE_HIGH
- val mChannel = NotificationChannel(channelId, name, importance)
- notificationManager.createNotificationChannel(mChannel)
- notificationManager.notify(FIRST_BOOT_NOTIFICATION_ID, notificationBuilder.build())
- }
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
index a81f5b5..92dc326 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
@@ -26,6 +26,8 @@ import androidx.fragment.app.add
import androidx.fragment.app.commit
import foundation.e.privacycentralapp.R
import foundation.e.privacycentralapp.features.dashboard.DashboardFragment
+import foundation.e.privacycentralapp.features.internetprivacy.InternetPrivacyFragment
+import foundation.e.privacycentralapp.features.location.FakeLocationFragment
import foundation.e.privacycentralapp.features.trackers.TrackersFragment
open class MainActivity : FragmentActivity(R.layout.activity_main) {
@@ -50,6 +52,12 @@ open class MainActivity : FragmentActivity(R.layout.activity_main) {
ACTION_VIEW_TRACKERS -> {
add<TrackersFragment>(R.id.container)
}
+ ACTION_VIEW_FAKE_LOCATION -> {
+ add<FakeLocationFragment>(R.id.container)
+ }
+ ACTION_VIEW_IPSCRAMBLING -> {
+ add<InternetPrivacyFragment>(R.id.container)
+ }
else -> add<DashboardFragment>(R.id.container)
}
disallowAddToBackStack()
@@ -69,6 +77,8 @@ open class MainActivity : FragmentActivity(R.layout.activity_main) {
companion object {
private const val ACTION_HIGHLIGHT_LEAKS = "ACTION_HIGHLIGHT_LEAKS"
private const val ACTION_VIEW_TRACKERS = "ACTION_VIEW_TRACKERS"
+ private const val ACTION_VIEW_FAKE_LOCATION = "ACTION_VIEW_FAKE_LOCATION"
+ private const val ACTION_VIEW_IPSCRAMBLING = "ACTION_VIEW_IPSCRAMBLING"
fun createHighlightLeaksIntent(context: Context, highlightIndex: Int) =
Intent(context, MainActivity::class.java).apply {
@@ -80,5 +90,17 @@ open class MainActivity : FragmentActivity(R.layout.activity_main) {
Intent(context, MainActivity::class.java).apply {
action = ACTION_VIEW_TRACKERS
}
+
+ fun createFakeLocationIntent(context: Context): Intent {
+ return Intent(context, MainActivity::class.java).apply {
+ action = ACTION_VIEW_FAKE_LOCATION
+ }
+ }
+
+ fun createIpScramblingIntent(context: Context): Intent {
+ return Intent(context, MainActivity::class.java).apply {
+ action = ACTION_VIEW_IPSCRAMBLING
+ }
+ }
}
}