From 8aca1315ebb0e2fa8ef299d65b7f58f2fcb50edb Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Thu, 24 Nov 2022 07:55:10 +0000 Subject: 5903 warn user on feature activation --- .../e/privacycentralapp/DependencyContainer.kt | 12 ++ .../e/privacycentralapp/common/WarningDialog.kt | 130 +++++++++++++++++++++ .../data/repositories/LocalStateRepository.kt | 17 ++- .../domain/entities/MainFeatures.kt | 22 ++++ .../domain/usecases/ShowFeaturesWarningUseCase.kt | 54 +++++++++ .../e/privacycentralapp/widget/WidgetUI.kt | 1 - 6 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/foundation/e/privacycentralapp/common/WarningDialog.kt create mode 100644 app/src/main/java/foundation/e/privacycentralapp/domain/entities/MainFeatures.kt create mode 100644 app/src/main/java/foundation/e/privacycentralapp/domain/usecases/ShowFeaturesWarningUseCase.kt (limited to 'app/src/main/java/foundation') diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index 345307c..f241e67 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -24,6 +24,7 @@ import androidx.lifecycle.DEFAULT_ARGS_KEY import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.CreationExtras +import foundation.e.privacycentralapp.common.WarningDialog import foundation.e.privacycentralapp.data.repositories.AppListsRepository import foundation.e.privacycentralapp.data.repositories.LocalStateRepository import foundation.e.privacycentralapp.data.repositories.TrackersRepository @@ -31,6 +32,7 @@ import foundation.e.privacycentralapp.domain.usecases.AppListUseCase import foundation.e.privacycentralapp.domain.usecases.FakeLocationStateUseCase import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase +import foundation.e.privacycentralapp.domain.usecases.ShowFeaturesWarningUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStateUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase import foundation.e.privacycentralapp.dummy.CityDataSource @@ -106,6 +108,10 @@ class DependencyContainer(val app: Application) { ) } + val showFeaturesWarningUseCase by lazy { + ShowFeaturesWarningUseCase(localStateRepository = localStateRepository) + } + val viewModelsFactory by lazy { ViewModelsFactory( getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase, @@ -126,6 +132,12 @@ class DependencyContainer(val app: Application) { UpdateTrackersWorker.periodicUpdate(context) + WarningDialog.startListening( + showFeaturesWarningUseCase, + GlobalScope, + context + ) + Widget.startListening( context, getQuickPrivacyStateUseCase, diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/WarningDialog.kt b/app/src/main/java/foundation/e/privacycentralapp/common/WarningDialog.kt new file mode 100644 index 0000000..cbbeffa --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/common/WarningDialog.kt @@ -0,0 +1,130 @@ +/* + * 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.common + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.util.Log +import android.view.View +import android.widget.CheckBox +import androidx.appcompat.app.AlertDialog +import foundation.e.privacycentralapp.PrivacyCentralApplication +import foundation.e.privacycentralapp.R +import foundation.e.privacycentralapp.domain.entities.MainFeatures +import foundation.e.privacycentralapp.domain.entities.MainFeatures.FAKE_LOCATION +import foundation.e.privacycentralapp.domain.entities.MainFeatures.IP_SCRAMBLING +import foundation.e.privacycentralapp.domain.entities.MainFeatures.TRACKERS_CONTROL +import foundation.e.privacycentralapp.domain.usecases.ShowFeaturesWarningUseCase +import foundation.e.privacycentralapp.main.MainActivity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map + +class WarningDialog : Activity() { + companion object { + private const val PARAM_FEATURE = "feature" + + fun startListening( + showFeaturesWarningUseCase: ShowFeaturesWarningUseCase, + appScope: CoroutineScope, + appContext: Context + ) { + showFeaturesWarningUseCase.showWarning().map { feature -> + appContext.startActivity( + createIntent(context = appContext, feature = feature) + ) + }.launchIn(appScope) + } + + private fun createIntent( + context: Context, + feature: MainFeatures, + ): Intent { + val intent = Intent(context, WarningDialog::class.java) + intent.putExtra(PARAM_FEATURE, feature.name) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + return intent + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + getWindow().setBackgroundDrawable(ColorDrawable(0)) + + val feature = try { + MainFeatures.valueOf(intent.getStringExtra(PARAM_FEATURE) ?: "") + } catch (e: Exception) { + Log.e("WarningDialog", "Missing mandatory activity parameter", e) + finish() + return + } + + showWarningDialog(feature) + } + + private fun showWarningDialog(feature: MainFeatures) { + val builder = AlertDialog.Builder(this) + builder.setOnDismissListener { finish() } + + val content: View = layoutInflater.inflate(R.layout.alertdialog_do_not_show_again, null) + val checkbox = content.findViewById(R.id.checkbox) + builder.setView(content) + + builder.setMessage( + when (feature) { + TRACKERS_CONTROL -> R.string.warningdialog_trackers_message + FAKE_LOCATION -> R.string.warningdialog_location_message + IP_SCRAMBLING -> R.string.warningdialog_ipscrambling_message + } + ) + + builder.setTitle( + when (feature) { + TRACKERS_CONTROL -> R.string.warningdialog_trackers_title + FAKE_LOCATION -> R.string.warningdialog_location_title + IP_SCRAMBLING -> R.string.warningdialog_ipscrambling_title + } + ) + + builder.setPositiveButton( + when (feature) { + IP_SCRAMBLING -> R.string.warningdialog_ipscrambling_cta + else -> R.string.ok + } + ) { _, _ -> + if (checkbox.isChecked()) { + (application as PrivacyCentralApplication) + .dependencyContainer.showFeaturesWarningUseCase + .doNotShowAgain(feature) + } + finish() + } + + if (feature == TRACKERS_CONTROL) { + builder.setNeutralButton(R.string.warningdialog_trackers_secondary_cta) { _, _ -> + startActivity(MainActivity.createTrackersIntent(this)) + finish() + } + } + + builder.show() + } +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt index 92ee0c1..6cb4f81 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt @@ -36,6 +36,9 @@ class LocalStateRepository(context: Context) { private const val KEY_FAKE_LATITUDE = "fakeLatitude" private const val KEY_FAKE_LONGITUDE = "fakeLongitude" private const val KEY_FIRST_BOOT = "firstBoot" + private const val KEY_HIDE_WARNING_TRACKERS = "hide_warning_trackers" + private const val KEY_HIDE_WARNING_LOCATION = "hide_warning_location" + private const val KEY_HIDE_WARNING_IPSCRAMBLING = "hide_warning_ipscrambling" } private val sharedPref = context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE) @@ -50,7 +53,7 @@ class LocalStateRepository(context: Context) { val areAllTrackersBlocked: MutableStateFlow = MutableStateFlow(false) - private val _fakeLocationEnabled = MutableStateFlow(sharedPref.getBoolean(KEY_FAKE_LOCATION, true)) + private val _fakeLocationEnabled = MutableStateFlow(sharedPref.getBoolean(KEY_FAKE_LOCATION, false)) val fakeLocationEnabled = _fakeLocationEnabled.asStateFlow() @@ -99,6 +102,18 @@ class LocalStateRepository(context: Context) { get() = sharedPref.getBoolean(KEY_FIRST_BOOT, true) set(value) = set(KEY_FIRST_BOOT, value) + var hideWarningTrackers: Boolean + get() = sharedPref.getBoolean(KEY_HIDE_WARNING_TRACKERS, false) + set(value) = set(KEY_HIDE_WARNING_TRACKERS, value) + + var hideWarningLocation: Boolean + get() = sharedPref.getBoolean(KEY_HIDE_WARNING_LOCATION, false) + set(value) = set(KEY_HIDE_WARNING_LOCATION, value) + + var hideWarningIpScrambling: Boolean + get() = sharedPref.getBoolean(KEY_HIDE_WARNING_IPSCRAMBLING, false) + set(value) = set(KEY_HIDE_WARNING_IPSCRAMBLING, value) + private fun set(key: String, value: Boolean) { sharedPref.edit().putBoolean(key, value).apply() } diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/MainFeatures.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/MainFeatures.kt new file mode 100644 index 0000000..0e7f99c --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/MainFeatures.kt @@ -0,0 +1,22 @@ +/* + * 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.domain.entities + +enum class MainFeatures { + TRACKERS_CONTROL, FAKE_LOCATION, IP_SCRAMBLING +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/ShowFeaturesWarningUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/ShowFeaturesWarningUseCase.kt new file mode 100644 index 0000000..e347b34 --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/ShowFeaturesWarningUseCase.kt @@ -0,0 +1,54 @@ +/* + * 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.domain.usecases + +import foundation.e.privacycentralapp.data.repositories.LocalStateRepository +import foundation.e.privacycentralapp.domain.entities.MainFeatures +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.dropWhile +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge + +class ShowFeaturesWarningUseCase( + private val localStateRepository: LocalStateRepository +) { + + fun showWarning(): Flow { + return merge( + localStateRepository.blockTrackers.drop(1).dropWhile { !it } + .filter { it && !localStateRepository.hideWarningTrackers } + .map { MainFeatures.TRACKERS_CONTROL }, + localStateRepository.fakeLocationEnabled.drop(1).dropWhile { !it } + .filter { it && !localStateRepository.hideWarningLocation } + .map { MainFeatures.FAKE_LOCATION }, + localStateRepository.ipScramblingSetting.drop(1).dropWhile { !it } + .filter { it && !localStateRepository.hideWarningIpScrambling } + .map { MainFeatures.IP_SCRAMBLING } + ) + } + + fun doNotShowAgain(feature: MainFeatures) { + when (feature) { + MainFeatures.TRACKERS_CONTROL -> localStateRepository.hideWarningTrackers = true + MainFeatures.FAKE_LOCATION -> localStateRepository.hideWarningLocation = true + MainFeatures.IP_SCRAMBLING -> localStateRepository.hideWarningIpScrambling = true + } + } +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt index 4fdbb03..fccfd48 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt @@ -184,7 +184,6 @@ fun render( ) setOnClickPendingIntent(R.id.graph_view_trackers_btn, pIntent) - // setOnClickPendingIntent(R.id.graph_view_trackers_btn_icon, pIntent) val graphHeightPx = 26.dpToPxF(context) val maxValue = -- cgit v1.2.1