diff options
| author | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2022-03-03 07:23:34 +0000 |
|---|---|---|
| committer | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2022-03-03 07:23:34 +0000 |
| commit | 72a66e8640c21683e99e4e7d866253fe205d14f0 (patch) | |
| tree | 17ee151ea3a5e5f90b8d48ecea88b78b6bb938f0 /app/src/main/java/foundation/e/privacycentralapp/features | |
| parent | ed659e60de259fe51b811af96a589c6bb9fd7d35 (diff) | |
| parent | e9e22d2fdbde4e9679337fa681d60b3fdbfeace7 (diff) | |
| download | advanced-privacy-72a66e8640c21683e99e4e7d866253fe205d14f0.tar.gz | |
Merge branch 'UX_upgrades' into 'main'
Ux upgrades
See merge request e/privacy-central/privacycentralapp!20
Diffstat (limited to 'app/src/main/java/foundation/e/privacycentralapp/features')
6 files changed, 106 insertions, 59 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt index e2eb58d..a606e49 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt @@ -22,11 +22,9 @@ import foundation.e.flowmvi.Actor import foundation.e.flowmvi.Reducer import foundation.e.flowmvi.SingleEventProducer import foundation.e.flowmvi.feature.BaseFeature +import foundation.e.privacycentralapp.domain.entities.AppWithCounts import foundation.e.privacycentralapp.domain.entities.TrackersPeriodicStatistics -import foundation.e.privacycentralapp.domain.usecases.AppListUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.Tracker import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf @@ -52,13 +50,12 @@ class TrackersFeature( val dayStatistics: TrackersPeriodicStatistics? = null, val monthStatistics: TrackersPeriodicStatistics? = null, val yearStatistics: TrackersPeriodicStatistics? = null, - val apps: List<ApplicationDescription>? = null, - val trackers: List<Tracker> = emptyList() + val apps: List<AppWithCounts>? = null, ) sealed class SingleEvent { data class ErrorEvent(val error: String) : SingleEvent() - data class OpenAppDetailsEvent(val appDesc: ApplicationDescription) : SingleEvent() + data class OpenAppDetailsEvent(val appDesc: AppWithCounts) : SingleEvent() object NewStatisticsAvailableSingleEvent : SingleEvent() } @@ -75,9 +72,9 @@ class TrackersFeature( val yearStatistics: TrackersPeriodicStatistics? = null ) : Effect() data class AvailableAppsListEffect( - val apps: List<ApplicationDescription> + val apps: List<AppWithCounts> ) : Effect() - data class OpenAppDetailsEffect(val appDesc: ApplicationDescription) : Effect() + data class OpenAppDetailsEffect(val appDesc: AppWithCounts) : Effect() object QuickPrivacyDisabledWarningEffect : Effect() data class ErrorEffect(val message: String) : Effect() object NewStatisticsAvailablesEffect : Effect() @@ -87,8 +84,7 @@ class TrackersFeature( fun create( initialState: State = State(), coroutineScope: CoroutineScope, - trackersStatisticsUseCase: TrackersStatisticsUseCase, - appListUseCase: AppListUseCase + trackersStatisticsUseCase: TrackersStatisticsUseCase ) = TrackersFeature( initialState, coroutineScope, reducer = { state, effect -> @@ -106,7 +102,19 @@ class TrackersFeature( }, actor = { state, action -> when (action) { - Action.InitAction -> merge<TrackersFeature.Effect>( + Action.InitAction -> merge<Effect>( + flowOf(Effect.NewStatisticsAvailablesEffect), + trackersStatisticsUseCase.listenUpdates().map { + Effect.NewStatisticsAvailablesEffect + } + ) + + is Action.ClickAppAction -> flowOf( + state.apps?.find { it.packageName == action.packageName }?.let { + Effect.OpenAppDetailsEffect(it) + } ?: run { Effect.ErrorEffect("Can't find back app.") } + ) + is Action.FetchStatistics -> merge<Effect>( flow { trackersStatisticsUseCase.getDayMonthYearStatistics() .let { (day, month, year) -> @@ -114,36 +122,15 @@ class TrackersFeature( Effect.TrackersStatisticsLoadedEffect( dayStatistics = day, monthStatistics = month, - yearStatistics = year + yearStatistics = year, ) ) } }, - appListUseCase.getAppsUsingInternet().map { apps -> - Effect.AvailableAppsListEffect(apps) - }, - trackersStatisticsUseCase.listenUpdates().map { - Effect.NewStatisticsAvailablesEffect + trackersStatisticsUseCase.getAppsWithCounts().map { + Effect.AvailableAppsListEffect(it) } ) - - is Action.ClickAppAction -> flowOf( - state.apps?.find { it.packageName == action.packageName }?.let { - Effect.OpenAppDetailsEffect(it) - } ?: run { Effect.ErrorEffect("Can't find back app.") } - ) - is Action.FetchStatistics -> flow { - trackersStatisticsUseCase.getDayMonthYearStatistics() - .let { (day, month, year) -> - emit( - Effect.TrackersStatisticsLoadedEffect( - dayStatistics = day, - monthStatistics = month, - yearStatistics = year, - ) - ) - } - } } }, singleEventProducer = { _, _, effect -> diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt index 3b22f89..0f686b4 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt @@ -18,10 +18,11 @@ package foundation.e.privacycentralapp.features.trackers import android.os.Bundle +import android.util.Log import android.view.View import android.widget.Toast -import androidx.fragment.app.add import androidx.fragment.app.commit +import androidx.fragment.app.replace import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager @@ -70,7 +71,7 @@ class TrackersFragment : } is TrackersFeature.SingleEvent.OpenAppDetailsEvent -> { requireActivity().supportFragmentManager.commit { - add<AppTrackersFragment>(R.id.container, args = AppTrackersFragment.buildArgs(event.appDesc.label.toString(), event.appDesc.packageName)) + replace<AppTrackersFragment>(R.id.container, args = AppTrackersFragment.buildArgs(event.appDesc.label.toString(), event.appDesc.packageName)) setReorderingAllowed(true) addToBackStack("apptrackers") } @@ -113,6 +114,7 @@ class TrackersFragment : } override fun onResume() { + Log.d("TestCounts", "OnResume") super.onResume() viewModel.submitAction(TrackersFeature.Action.FetchStatistics) } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt index e3a97cc..c2a1822 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt @@ -20,15 +20,13 @@ package foundation.e.privacycentralapp.features.trackers import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import foundation.e.privacycentralapp.common.Factory -import foundation.e.privacycentralapp.domain.usecases.AppListUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch class TrackersViewModel( - private val trackersStatisticsUseCase: TrackersStatisticsUseCase, - private val appListUseCase: AppListUseCase + private val trackersStatisticsUseCase: TrackersStatisticsUseCase ) : ViewModel() { private val _actions = MutableSharedFlow<TrackersFeature.Action>() @@ -38,8 +36,7 @@ class TrackersViewModel( TrackersFeature.create( coroutineScope = viewModelScope, - trackersStatisticsUseCase = trackersStatisticsUseCase, - appListUseCase = appListUseCase + trackersStatisticsUseCase = trackersStatisticsUseCase ) } @@ -51,11 +48,10 @@ class TrackersViewModel( } class TrackersViewModelFactory( - private val trackersStatisticsUseCase: TrackersStatisticsUseCase, - private val appListUseCase: AppListUseCase + private val trackersStatisticsUseCase: TrackersStatisticsUseCase ) : Factory<TrackersViewModel> { override fun create(): TrackersViewModel { - return TrackersViewModel(trackersStatisticsUseCase, appListUseCase) + return TrackersViewModel(trackersStatisticsUseCase) } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt index 16cd4a0..b35fbca 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt @@ -17,6 +17,7 @@ package foundation.e.privacycentralapp.features.trackers.apptrackers +import android.net.Uri import android.util.Log import foundation.e.flowmvi.Actor import foundation.e.flowmvi.Reducer @@ -63,17 +64,24 @@ class AppTrackersFeature( return null } } + + fun getTrackersCount() = trackers?.size ?: 0 + fun getBlockedTrackersCount(): Int = if (isBlockingActivated) + getTrackersCount() - (whitelist?.size ?: 0) + else 0 } sealed class SingleEvent { data class ErrorEvent(val error: Any) : SingleEvent() object NewStatisticsAvailableSingleEvent : SingleEvent() + data class OpenUrlEvent(val url: Uri) : SingleEvent() } sealed class Action { data class InitAction(val packageName: String) : Action() data class BlockAllToggleAction(val isBlocked: Boolean) : Action() data class ToggleTrackerAction(val tracker: Tracker, val isBlocked: Boolean) : Action() + data class ClickTracker(val tracker: Tracker) : Action() object FetchStatistics : Action() } @@ -87,9 +95,12 @@ class AppTrackersFeature( object NewStatisticsAvailablesEffect : Effect() data class QuickPrivacyUpdatedEffect(val enabled: Boolean) : Effect() object QuickPrivacyDisabledWarningEffect : Effect() + data class OpenUrlEffect(val url: Uri) : Effect() } companion object { + + private const val exodusBaseUrl = "https://reports.exodus-privacy.eu.org/fr/trackers/" fun create( initialState: State = State(), coroutineScope: CoroutineScope, @@ -179,6 +190,17 @@ class AppTrackersFeature( } ?: run { flowOf(Effect.ErrorEffect("No appDesc.")) } } else flowOf(Effect.NoEffect) } + is Action.ClickTracker -> { + flowOf( + action.tracker.getExodusId()?.let { + try { + Effect.OpenUrlEffect(Uri.parse(exodusBaseUrl + it)) + } catch (e: Exception) { + Effect.ErrorEffect("Invalid Url") + } + } ?: Effect.NoEffect + ) + } is Action.FetchStatistics -> flowOf( state.appDesc?.uid?.let { Effect.AvailableTrackersListEffect( @@ -196,9 +218,17 @@ class AppTrackersFeature( SingleEvent.ErrorEvent(R.string.apptrackers_error_quickprivacy_disabled) is Effect.NewStatisticsAvailablesEffect -> SingleEvent.NewStatisticsAvailableSingleEvent + is Effect.OpenUrlEffect -> + SingleEvent.OpenUrlEvent(effect.url) else -> null } } ) } } + +fun Tracker.getExodusId(): String? { + return if (id.startsWith("exodus_")) { + id.substringAfter("exodus_") + } else null +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt index 440edf7..8e2dc3b 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt @@ -17,6 +17,8 @@ package foundation.e.privacycentralapp.features.trackers.apptrackers +import android.content.ActivityNotFoundException +import android.content.Intent import android.os.Bundle import android.view.View import android.widget.Toast @@ -76,6 +78,12 @@ class AppTrackersFragment : is SingleEvent.NewStatisticsAvailableSingleEvent -> { viewModel.submitAction(Action.FetchStatistics) } + is SingleEvent.OpenUrlEvent -> + try { + startActivity(Intent(Intent.ACTION_VIEW, event.url)) + } catch (e: ActivityNotFoundException) { + displayToast("No application to see webpages") + } } } } @@ -104,14 +112,13 @@ class AppTrackersFragment : binding.trackers.apply { layoutManager = LinearLayoutManager(requireContext()) setHasFixedSize(true) - adapter = ToggleTrackersAdapter(R.layout.apptrackers_item_tracker_toggle) { tracker, isBlocked -> - viewModel.submitAction( - Action.ToggleTrackerAction( - tracker, - isBlocked - ) - ) - } + adapter = ToggleTrackersAdapter( + R.layout.apptrackers_item_tracker_toggle, + onToggleSwitch = { tracker, isBlocked -> + viewModel.submitAction(Action.ToggleTrackerAction(tracker, isBlocked)) + }, + onClickTitle = { viewModel.submitAction(Action.ClickTracker(it)) } + ) } } @@ -121,6 +128,14 @@ class AppTrackersFragment : } override fun render(state: State) { + + binding.trackersCountSummary.text = if (state.getTrackersCount() == 0) "" + else getString( + R.string.apptrackers_trackers_count_summary, + state.getBlockedTrackersCount(), + state.getTrackersCount() + ) + binding.blockAllToggle.isChecked = state.isBlockingActivated binding.trackersListTitle.isVisible = state.isBlockingActivated diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt index 580a60c..82f2d2c 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt @@ -17,11 +17,14 @@ package foundation.e.privacycentralapp.features.trackers.apptrackers +import android.text.SpannableString +import android.text.style.UnderlineSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Switch import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import foundation.e.privacycentralapp.R @@ -29,9 +32,9 @@ import foundation.e.privacymodules.trackers.Tracker class ToggleTrackersAdapter( private val itemsLayout: Int, - private val listener: (Tracker, Boolean) -> Unit -) : - RecyclerView.Adapter<ToggleTrackersAdapter.ViewHolder>() { + private val onToggleSwitch: (Tracker, Boolean) -> Unit, + private val onClickTitle: (Tracker) -> Unit +) : RecyclerView.Adapter<ToggleTrackersAdapter.ViewHolder>() { var isEnabled = true @@ -42,7 +45,17 @@ class ToggleTrackersAdapter( val toggleOverlay: View = view.findViewById(R.id.toggle_clicker) fun bind(item: Pair<Tracker, Boolean>, isEnabled: Boolean) { - title.text = item.first.label + val text = item.first.label + if (item.first.website != null) { + title.setTextColor(ContextCompat.getColor(title.context, R.color.accent)) + val spannable = SpannableString(text) + spannable.setSpan(UnderlineSpan(), 0, spannable.length, 0) + title.text = spannable + } else { + title.setTextColor(ContextCompat.getColor(title.context, R.color.black)) + title.text = text + } + toggle.isChecked = item.second toggle.isEnabled = isEnabled toggleOverlay.isVisible = !isEnabled @@ -62,10 +75,14 @@ class ToggleTrackersAdapter( .inflate(itemsLayout, parent, false) val holder = ViewHolder(view) holder.toggle.setOnClickListener { - listener(dataSet[holder.adapterPosition].first, holder.toggle.isChecked) + onToggleSwitch(dataSet[holder.adapterPosition].first, holder.toggle.isChecked) } holder.toggleOverlay.setOnClickListener { - listener(dataSet[holder.adapterPosition].first, false) + onToggleSwitch(dataSet[holder.adapterPosition].first, false) + } + + holder.title.setOnClickListener { + onClickTitle(dataSet[holder.adapterPosition].first) } return holder |
