From c356af90b1d8dcff53f5f1dae65fe19cc03b2461 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Mon, 28 Nov 2022 18:24:21 +0100 Subject: [PATCH 1/4] 5538: Notification while fake locaiton or ip scrambling running. --- .idea/copyright/MURENA.xml | 6 + .idea/copyright/profiles_settings.xml | 3 - .../privacycentralapp/DependencyContainer.kt | 8 + .../e/privacycentralapp/Notifications.kt | 196 ++++++++++++++++++ .../common/BootCompletedReceiver.kt | 41 +--- .../e/privacycentralapp/main/MainActivity.kt | 22 ++ app/src/main/res/drawable/ic_fmd_bad.xml | 15 ++ app/src/main/res/drawable/ic_language.xml | 9 + app/src/main/res/values/strings.xml | 13 ++ .../java/android/app/NotificationChannel.java | 34 +++ .../permissions/PermissionsPrivacyModule.kt | 11 + .../permissions/PermissionsPrivacyModule.kt | 3 + .../permissions/IPermissionsPrivacyModule.kt | 11 + 13 files changed, 330 insertions(+), 42 deletions(-) create mode 100644 .idea/copyright/MURENA.xml delete mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 app/src/main/java/foundation/e/privacycentralapp/Notifications.kt create mode 100644 app/src/main/res/drawable/ic_fmd_bad.xml create mode 100644 app/src/main/res/drawable/ic_language.xml create mode 100644 permissionse/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java diff --git a/.idea/copyright/MURENA.xml b/.idea/copyright/MURENA.xml new file mode 100644 index 00000000..0c0ee957 --- /dev/null +++ b/.idea/copyright/MURENA.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e9f8efd4..00000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index f241e673..e6d4c42e 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 00000000..f9e2e7c4 --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2022 E FOUNDATION + * + * 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.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 pendingIntent = PendingIntent.getActivity( + context, + 0, + context.packageManager.getLaunchIntentForPackage(context.packageName), + PendingIntent.FLAG_IMMUTABLE + ) + val notificationBuilder: NotificationCompat.Builder = + NotificationCompat.Builder(context, CHANNEL_FIRST_BOOT) + .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)) + ) + 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, + "First Boot", + 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, + destinationIntent = MainActivity.createFakeLocationIntent(context), + id = NOTIFICATION_FAKE_LOCATION_FLAG, + channelId = CHANNEL_FAKE_LOCATION_FLAG, + icon = R.drawable.ic_fmd_bad, + title = R.string.notifications_fake_location_title, + content = R.string.notifications_fake_location_content + ) + MainFeatures.IP_SCRAMBLING -> showFlagNotification( + context = context, + destinationIntent = MainActivity.createIpScramblingIntent(context), + id = NOTIFICATION_IPSCRAMBLING_FLAG, + channelId = CHANNEL_IPSCRAMBLING_FLAG, + icon = R.drawable.ic_language, + title = R.string.notifications_ipscrambling_title, + content = R.string.notifications_ipscrambling_content + ) + else -> {} + } + } + + @Suppress("LongParameterList") + private fun showFlagNotification( + context: Context, + destinationIntent: Intent, + id: Int, + channelId: String, + icon: Int, + title: Int, + content: Int + ) { + destinationIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + val pendingIntent: PendingIntent = PendingIntent.getActivity( + context, 0, destinationIntent, PendingIntent.FLAG_IMMUTABLE + ) + + val builder = NotificationCompat.Builder(context, channelId) + .setSmallIcon(icon) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setContentIntent(pendingIntent) + .setContentTitle(context.getString(title)) + .setStyle( + NotificationCompat.BigTextStyle().bigText(context.getString(content)) + ) + .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) + } +} 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 f43c2cce..d7902ee5 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 a81f5b5f..92dc3269 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(R.id.container) } + ACTION_VIEW_FAKE_LOCATION -> { + add(R.id.container) + } + ACTION_VIEW_IPSCRAMBLING -> { + add(R.id.container) + } else -> add(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 + } + } } } diff --git a/app/src/main/res/drawable/ic_fmd_bad.xml b/app/src/main/res/drawable/ic_fmd_bad.xml new file mode 100644 index 00000000..beeff356 --- /dev/null +++ b/app/src/main/res/drawable/ic_fmd_bad.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_language.xml b/app/src/main/res/drawable/ic_language.xml new file mode 100644 index 00000000..ae37ec03 --- /dev/null +++ b/app/src/main/res/drawable/ic_language.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4d6e1f94..6db073fc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -102,6 +102,19 @@ This option uses the TOR network to hide your real IP address. Only use it for specific applications. While your IP is faked, your Internet speed is likely to be reduced.\nImportant: we advise disabling this feature for email services as your address may end into permanent ban from your provider. I understand + + Fake location flag + Highlight that location is currently faked by Advanced Privacy + + Fake location is on + All apps using your location including your navigation app are now using this fake location. + + Hide my IP flag + Highlight that IP is currently hidden by Advanced Privacy + Real IP hidden + This could impact the functioning of some applications. + + @string/app_name Your online privacy is protected diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java new file mode 100644 index 00000000..9e8d65a9 --- /dev/null +++ b/permissionse/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java @@ -0,0 +1,34 @@ +/* + * 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 android.app; + +import android.annotation.TargetApi; + +import androidx.annotation.DeprecatedSinceApi; + +public class NotificationChannel { + + @TargetApi(29) + @DeprecatedSinceApi(api = 30, message = "Use setBlockable() instead.") + public void setBlockableSystem(boolean blockableSystem) {} + + // Public in API 33. + @TargetApi(30) + public void setBlockable(boolean blockableSystem) {} + +} diff --git a/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt b/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt index 83711dd0..c07f367b 100644 --- a/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt +++ b/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt @@ -21,6 +21,7 @@ import android.annotation.TargetApi import android.app.AppOpsManager import android.app.AppOpsManager.OP_NONE import android.app.AppOpsManager.strOpToOp +import android.app.NotificationChannel import android.content.Context import android.net.IConnectivityManager import android.net.VpnManager @@ -191,4 +192,14 @@ class PermissionsPrivacyModule(context: Context) : APermissionsPrivacyModule(con return null } } + + override fun setBlockable(notificationChannel: NotificationChannel) { + when (Build.VERSION.SDK_INT) { + 29 -> notificationChannel.setBlockableSystem(true) + 30, 31, 32 -> notificationChannel.setBlockable(true) + else -> { + Log.e("Permissions-e", "Bad android sdk version") + } + } + } } diff --git a/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt b/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt index 98ebdc3b..52dfd08b 100644 --- a/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt +++ b/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt @@ -17,6 +17,7 @@ package foundation.e.privacymodules.permissions +import android.app.NotificationChannel import android.content.Context import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription @@ -48,4 +49,6 @@ class PermissionsPrivacyModule(context: Context) : APermissionsPrivacyModule(con override fun getAlwaysOnVpnPackage(): String? { return null } + + override fun setBlockable(notificationChannel: NotificationChannel) {} } diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt index 6624798e..ff0b3d7c 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt +++ b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt @@ -17,6 +17,7 @@ package foundation.e.privacymodules.permissions +import android.app.NotificationChannel import android.content.pm.ApplicationInfo import android.graphics.drawable.Drawable import foundation.e.privacymodules.permissions.data.AppOpModes @@ -128,4 +129,14 @@ interface IPermissionsPrivacyModule { * Returns the package name of the currently set always-on VPN application, or null. */ fun getAlwaysOnVpnPackage(): String? + + /** + * Allows users to block notifications sent through this channel, if this channel belongs to + * a package that is signed with the system signature. + * + * If the channel does not belong to a package that is signed with the system signature, this + * method does nothing, since such channels are blockable by default and cannot be set to be + * unblockable. + */ + fun setBlockable(notificationChannel: NotificationChannel) } -- GitLab From a6fc4bf0d1011d0a73810d762a89e364e630f209 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Sat, 3 Dec 2022 11:55:33 +0100 Subject: [PATCH 2/4] Fix all notif canceled when stopping ipscrambling --- .idea/copyright/E_Foundation.xml | 6 ----- app/build.gradle | 2 +- .../e/privacycentralapp/Notifications.kt | 2 +- app/src/main/res/values/strings.xml | 25 ++++++++++--------- build.gradle | 3 +-- spotless/copyright.kt | 17 ------------- 6 files changed, 16 insertions(+), 39 deletions(-) delete mode 100644 .idea/copyright/E_Foundation.xml delete mode 100644 spotless/copyright.kt diff --git a/.idea/copyright/E_Foundation.xml b/.idea/copyright/E_Foundation.xml deleted file mode 100644 index 6f4df2f8..00000000 --- a/.idea/copyright/E_Foundation.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 323a1186..12d11590 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt b/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt index f9e2e7c4..09bf5598 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 E FOUNDATION + * 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 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6db073fc..64b2d421 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -102,18 +102,6 @@ This option uses the TOR network to hide your real IP address. Only use it for specific applications. While your IP is faked, your Internet speed is likely to be reduced.\nImportant: we advise disabling this feature for email services as your address may end into permanent ban from your provider. I understand - - Fake location flag - Highlight that location is currently faked by Advanced Privacy - - Fake location is on - All apps using your location including your navigation app are now using this fake location. - - Hide my IP flag - Highlight that IP is currently hidden by Advanced Privacy - Real IP hidden - This could impact the functioning of some applications. - @string/app_name @@ -132,6 +120,19 @@ @string/dashboard_state_ipaddress_on @string/dashboard_graph_trackers_legend View + + Discover Advanced Privacy Tap to find out how to easily block trackers, fake your location & hide your IP address. + Fake location flag + Highlight that location is currently faked by Advanced Privacy + + Fake location is on + All apps using your location including your navigation app are now using this fake location. + + Hide my IP flag + Highlight that IP is currently hidden by Advanced Privacy + Real IP hidden + This could impact the functioning of some applications. + diff --git a/build.gradle b/build.gradle index 2abc73d2..83197c30 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ buildscript { 'targetSdk' : 31, 'version' : [ 'major': 1, - 'minor': 4, + 'minor': 7, 'patch': 0, ], ] @@ -131,7 +131,6 @@ subprojects { targetExclude('bin/**/*.kt') targetExclude '**/spotless/*.kt' ktlint(Versions.ktlint) - licenseHeaderFile rootProject.file('spotless/copyright.kt') } format 'misc', { // define the files to apply `misc` to diff --git a/spotless/copyright.kt b/spotless/copyright.kt deleted file mode 100644 index 698a4636..00000000 --- a/spotless/copyright.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) $YEAR E FOUNDATION - * - * 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 . - */ - -- GitLab From 7ac2ea9577b0b9a22e2e4fd0f6f8c35e5c9cf3f0 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Tue, 6 Dec 2022 05:33:32 +0100 Subject: [PATCH 3/4] MR suggestion, fix constants keys case. --- .idea/copyright/profiles_settings.xml | 3 +++ .../main/java/foundation/e/privacycentralapp/Notifications.kt | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 .idea/copyright/profiles_settings.xml diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 00000000..e9f8efd4 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt b/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt index 09bf5598..6e881ebc 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt @@ -38,8 +38,8 @@ 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 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 -- GitLab From 9f79966f1fbae83f585918b2528add79aab232d6 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Tue, 6 Dec 2022 06:28:33 +0100 Subject: [PATCH 4/4] MR suggestion, refact notification builder. --- .../e/privacycentralapp/Notifications.kt | 100 ++++++++++-------- app/src/main/res/values/strings.xml | 1 + 2 files changed, 58 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt b/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt index 6e881ebc..0df3e180 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/Notifications.kt @@ -47,22 +47,19 @@ object Notifications { fun showFirstBootNotification(context: Context) { createNotificationFirstBootChannel(context) - val pendingIntent = PendingIntent.getActivity( + val notificationBuilder: NotificationCompat.Builder = notificationBuilder( context, - 0, - context.packageManager.getLaunchIntentForPackage(context.packageName), - PendingIntent.FLAG_IMMUTABLE + 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) + ) ) - val notificationBuilder: NotificationCompat.Builder = - NotificationCompat.Builder(context, CHANNEL_FIRST_BOOT) - .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)) - ) + .setAutoCancel(true) + NotificationManagerCompat.from(context).notify( NOTIFICATION_FIRST_BOOT, notificationBuilder.build() ) @@ -112,7 +109,7 @@ object Notifications { private fun createNotificationFirstBootChannel(context: Context) { val channel = NotificationChannel( CHANNEL_FIRST_BOOT, - "First Boot", + context.getString(R.string.notifications_first_boot_channel_name), NotificationManager.IMPORTANCE_HIGH ) NotificationManagerCompat.from(context).createNotificationChannel(channel) @@ -137,49 +134,37 @@ object Notifications { when (feature) { MainFeatures.FAKE_LOCATION -> showFlagNotification( context = context, - destinationIntent = MainActivity.createFakeLocationIntent(context), id = NOTIFICATION_FAKE_LOCATION_FLAG, - channelId = CHANNEL_FAKE_LOCATION_FLAG, - icon = R.drawable.ic_fmd_bad, - title = R.string.notifications_fake_location_title, - content = R.string.notifications_fake_location_content + 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, - destinationIntent = MainActivity.createIpScramblingIntent(context), id = NOTIFICATION_IPSCRAMBLING_FLAG, - channelId = CHANNEL_IPSCRAMBLING_FLAG, - icon = R.drawable.ic_language, - title = R.string.notifications_ipscrambling_title, - content = R.string.notifications_ipscrambling_content + 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 -> {} } } - @Suppress("LongParameterList") private fun showFlagNotification( context: Context, - destinationIntent: Intent, id: Int, - channelId: String, - icon: Int, - title: Int, - content: Int + content: NotificationContent, ) { - destinationIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - val pendingIntent: PendingIntent = PendingIntent.getActivity( - context, 0, destinationIntent, PendingIntent.FLAG_IMMUTABLE - ) - - val builder = NotificationCompat.Builder(context, channelId) - .setSmallIcon(icon) + val builder = notificationBuilder(context, content) .setPriority(NotificationCompat.PRIORITY_LOW) - .setContentIntent(pendingIntent) - .setContentTitle(context.getString(title)) - .setStyle( - NotificationCompat.BigTextStyle().bigText(context.getString(content)) - ) .setOngoing(true) NotificationManagerCompat.from(context).notify(id, builder.build()) @@ -193,4 +178,33 @@ object Notifications { } 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/res/values/strings.xml b/app/src/main/res/values/strings.xml index 64b2d421..b29bbcad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -122,6 +122,7 @@ View + First Boot Discover Advanced Privacy Tap to find out how to easily block trackers, fake your location & hide your IP address. Fake location flag -- GitLab