diff options
Diffstat (limited to 'app')
15 files changed, 243 insertions, 75 deletions
diff --git a/app/build.gradle b/app/build.gradle index b771285..3716977 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -110,8 +110,8 @@ dependencies { //googleImplementation project(":privacymodulesgoogle") // include the e specific version of the modules, just for the e flavor - implementation 'foundation.e:privacymodule.trackerfilter:0.3.0' - implementation 'foundation.e:privacymodule.api:0.5.0' + implementation 'foundation.e:privacymodule.trackerfilter:0.4.0' + implementation 'foundation.e:privacymodule.api:0.6.0' e29Implementation 'foundation.e:privacymodule.e-29:0.4.2' e30Implementation 'foundation.e:privacymodule.e-30:0.4.2' implementation 'foundation.e:privacymodule.tor:0.2.1' diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index 76a9539..6b4035e 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -89,7 +89,7 @@ class DependencyContainer constructor(val app: Application) { private val appListUseCase = AppListUseCase(appListsRepository) private val trackersStatisticsUseCase by lazy { - TrackersStatisticsUseCase(trackTrackersPrivacyModule, appListsRepository, context.resources) + TrackersStatisticsUseCase(trackTrackersPrivacyModule, blockTrackersPrivacyModule, appListsRepository, context.resources) } private val trackersStateUseCase by lazy { @@ -119,7 +119,7 @@ class DependencyContainer constructor(val app: Application) { } val trackersViewModelFactory by lazy { - TrackersViewModelFactory(trackersStatisticsUseCase, appListUseCase) + TrackersViewModelFactory(trackersStatisticsUseCase) } val appTrackersViewModelFactory by lazy { diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt index d66ce76..07cf125 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt @@ -24,7 +24,7 @@ import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import foundation.e.privacycentralapp.R -import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.privacycentralapp.domain.entities.AppWithCounts class AppsAdapter( private val itemsLayout: Int, @@ -34,15 +34,20 @@ class AppsAdapter( class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val appName: TextView = view.findViewById(R.id.title) - - fun bind(item: ApplicationDescription) { + val counts: TextView = view.findViewById(R.id.counts) + val icon: ImageView = view.findViewById(R.id.icon) + fun bind(item: AppWithCounts) { appName.text = item.label - - itemView.findViewById<ImageView>(R.id.icon).setImageDrawable(item.icon) + counts.text = itemView.context.getString( + R.string.trackers_app_trackers_counts, + item.blockedTrackersCount, + item.trackersCount + ) + icon.setImageDrawable(item.icon) } } - var dataSet: List<ApplicationDescription> = emptyList() + var dataSet: List<AppWithCounts> = emptyList() set(value) { field = value notifyDataSetChanged() diff --git a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt index 4718923..958a536 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt @@ -50,6 +50,14 @@ class AppListsRepository( return appDescriptions.value.second } + fun foldForHiddenSystemApp(appUid: Int, appValueGetter: (Int) -> Int): Int { + return if (appUid == dummySystemApp.uid) { + getHiddenSystemApps().fold(0) { acc, app -> + acc + appValueGetter(app.uid) + } + } else appValueGetter(appUid) + } + private val pm get() = context.packageManager private val appDescriptions = MutableStateFlow( diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt new file mode 100644 index 0000000..682dfc8 --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt @@ -0,0 +1,50 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +package foundation.e.privacycentralapp.domain.entities + +import android.graphics.drawable.Drawable +import foundation.e.privacymodules.permissions.data.ApplicationDescription + +data class AppWithCounts( + val packageName: String, + val uid: Int, + var label: CharSequence?, + var icon: Drawable?, + val isWhitelisted: Boolean = false, + val trackersCount: Int = 0, + val whiteListedTrackersCount: Int = 0 +) { + constructor( + app: ApplicationDescription, + isWhitelisted: Boolean, + trackersCount: Int, + whiteListedTrackersCount: Int + ) : + this( + packageName = app.packageName, + uid = app.uid, + label = app.label, + icon = app.icon, + isWhitelisted = isWhitelisted, + trackersCount = trackersCount, + whiteListedTrackersCount = whiteListedTrackersCount + ) + + val blockedTrackersCount get() = if (isWhitelisted) 0 + else trackersCount - whiteListedTrackersCount +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt index 9a8b12a..ad8f565 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt @@ -20,18 +20,22 @@ package foundation.e.privacycentralapp.domain.usecases import android.content.res.Resources import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.data.repositories.AppListsRepository +import foundation.e.privacycentralapp.domain.entities.AppWithCounts import foundation.e.privacycentralapp.domain.entities.TrackersPeriodicStatistics +import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule import foundation.e.privacymodules.trackers.Tracker import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.map import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit class TrackersStatisticsUseCase( private val trackTrackersPrivacyModule: ITrackTrackersPrivacyModule, + private val blockTrackersPrivacyModule: IBlockTrackersPrivacyModule, private val appListsRepository: AppListsRepository, private val resources: Resources ) { @@ -124,4 +128,23 @@ class TrackersStatisticsUseCase( return trackers.sortedBy { it.label.lowercase() } } + + fun getAppsWithCounts(): Flow<List<AppWithCounts>> { + val trackersCounts = trackTrackersPrivacyModule.getTrackersCountByApp() + return appListsRepository.getVisibleApps() + .map { apps -> + apps.map { app -> + AppWithCounts( + app, + blockTrackersPrivacyModule.isWhitelisted(app.uid), + appListsRepository.foldForHiddenSystemApp(app.uid) { + trackersCounts.getOrDefault(it, 0) + }, + appListsRepository.foldForHiddenSystemApp(app.uid) { + blockTrackersPrivacyModule.getWhiteList(it).size + } + ) + } + } + } } 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 diff --git a/app/src/main/res/layout/apptrackers_fragment.xml b/app/src/main/res/layout/apptrackers_fragment.xml index 13f58e3..17d992e 100644 --- a/app/src/main/res/layout/apptrackers_fragment.xml +++ b/app/src/main/res/layout/apptrackers_fragment.xml @@ -41,6 +41,13 @@ android:layout_width="match_parent" android:orientation="vertical" > + <TextView + android:id="@+id/trackers_count_summary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="16sp" + android:padding="16dp" + /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/app/src/main/res/layout/trackers_item_app.xml b/app/src/main/res/layout/trackers_item_app.xml index b368664..310c792 100644 --- a/app/src/main/res/layout/trackers_item_app.xml +++ b/app/src/main/res/layout/trackers_item_app.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.appcompat.widget.LinearLayoutCompat - xmlns:android="http://schemas.android.com/apk/res/android" +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/container" android:layout_height="52dp" android:layout_width="match_parent" @@ -16,23 +16,49 @@ android:layout_height="32dp" android:layout_width="32dp" android:src="@drawable/ic_facebook" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" /> <TextView android:id="@+id/title" - android:layout_centerVertical="true" android:layout_height="wrap_content" android:layout_width="0dp" - android:layout_weight="1" android:maxLines="1" android:ellipsize="end" android:layout_marginStart="16dp" android:textSize="14sp" + android:textColor="@color/black_text" tools:text="Body sensor" + app:layout_constraintLeft_toRightOf="@+id/icon" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@+id/counts" + app:layout_constraintRight_toLeftOf="@+id/arrow" + app:layout_constraintVertical_chainStyle="packed" + /> + <TextView + android:id="@+id/counts" + android:layout_height="wrap_content" + android:layout_width="0dp" + android:maxLines="1" + android:ellipsize="end" + android:layout_marginStart="16dp" + android:textSize="14sp" + android:textColor="@color/grey_text_2" + tools:text="1 tracker blocked out of 4" + app:layout_constraintLeft_toRightOf="@+id/icon" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@+id/title" + app:layout_constraintRight_toLeftOf="@+id/arrow" /> <ImageView + android:id="@+id/arrow" android:layout_width="24dp" android:layout_height="24dp" android:src="@drawable/ic_chevron_right_24dp" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" /> -</androidx.appcompat.widget.LinearLayoutCompat> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8c2a0ba..49b6c95 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,7 +40,7 @@ <string name="ipscrambling_hide_ip_label">Hide IP address</string> <string name="ipscrambling_hide_ip_subtitle">I am anonymous on the internet</string> <string name="ipscrambling_tor_disclaimer"><b>Note:</b> when active, this setting will slow down your Internet connectivity speed (uses the Tor network).</string> - <string name="ipscrambling_location_label">Force a country of origin:</string> + <string name="ipscrambling_location_label">My internet activity must appear from:</string> <string name="ipscrambling_any_location">Random country</string> <string name="ipscrambling_select_app">Apply this setting to all selected apps:</string> <string name="ipscrambling_error_quickprivacy_disabled">Enabled Quick Privacy to use functionalities</string> @@ -67,6 +67,7 @@ <string name="trackers_graph_hours_period_format">HH:mm</string> <string name="trackers_graph_days_period_format">MMM d - EEE</string> <string name="trackers_graph_months_period_format">MMM yyyy</string> + <string name="trackers_app_trackers_counts">%1$d blocked trackers out of %2$d</string> <!-- App Trackers --> <string name="apptrackers_block_all_toggle">Block trackers</string> @@ -74,6 +75,7 @@ <string name="apptrackers_no_trackers_yet_block_off">No trackers were detected yet. If new trackers are detected they will be updated here.</string> <string name="apptrackers_no_trackers_yet_block_on">No trackers were detected yet. All future trackers will be blocked.</string> <string name="apptrackers_error_quickprivacy_disabled">Enable Quick Privacy to be able to activate/deactivate trackers.</string> + <string name="apptrackers_trackers_count_summary">%1$d blocked trackers out of %2$d detected trackers</string> <!-- --> <string name="quick_protection_info">Quick protection enables these settings when turned on</string> |