Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3e73c61b authored by Guillaume Jacquart's avatar Guillaume Jacquart
Browse files

5538 notifications for fake loc ipscrambling

parent c337df61
Loading
Loading
Loading
Loading

.idea/copyright/E_Foundation.xml

deleted100644 → 0
+0 −6
Original line number Diff line number Diff line
<component name="CopyrightManager">
  <copyright>
    <option name="notice" value="Copyright (C) &amp;#36;today.year E FOUNDATION&#10;&#10;This program is free software: you can redistribute it and/or modify&#10;it under the terms of the GNU General Public License as published by&#10;the Free Software Foundation, either version 3 of the License, or&#10;(at your option) any later version.&#10;&#10;This program is distributed in the hope that it will be useful,&#10;but WITHOUT ANY WARRANTY; without even the implied warranty of&#10;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&#10;GNU General Public License for more details.&#10;&#10;You should have received a copy of the GNU General Public License&#10;along with this program.  If not, see &lt;https://www.gnu.org/licenses/&gt;." />
    <option name="myName" value="E_Foundation" />
  </copyright>
</component>
 No newline at end of file
+6 −0
Original line number Diff line number Diff line
<component name="CopyrightManager">
  <copyright>
    <option name="notice" value="Copyright (C) &amp;#36;today.year MURENA SAS&#10;&#10;This program is free software: you can redistribute it and/or modify&#10;it under the terms of the GNU General Public License as published by&#10;the Free Software Foundation, either version 3 of the License, or&#10;(at your option) any later version.&#10;&#10;This program is distributed in the hope that it will be useful,&#10;but WITHOUT ANY WARRANTY; without even the implied warranty of&#10;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&#10;GNU General Public License for more details.&#10;&#10;You should have received a copy of the GNU General Public License&#10;along with this program.  If not, see &lt;https://www.gnu.org/licenses/&gt;." />
    <option name="myName" value="MURENA" />
  </copyright>
</component>
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
@@ -132,7 +132,7 @@ dependencies {

    implementation project(':trackers')

    implementation 'foundation.e:privacymodule.tor:1.6.0-dev-orbot-16.6.2'
    implementation 'foundation.e:privacymodule.tor:1.7.0-orbot-16.6.2'

    implementation 'foundation.e:elib:0.0.1-alpha11'

+8 −0
Original line number Diff line number Diff line
@@ -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,
+210 −0
Original line number Diff line number Diff line
/*
 * 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
    }
}
Loading